Compare commits

..

No commits in common. "6ec3e96909584cc94406eaa816bc4c9791220cb1" and "da71de6806b547c7593e5bb89dd8207bfac87853" have entirely different histories.

64 changed files with 1111 additions and 1422 deletions

View File

@ -5,6 +5,7 @@ name: test
steps: steps:
- name: AnylyseBuildTest - name: AnylyseBuildTest
image: mingc/android-build-box:latest image: mingc/android-build-box:latest
failure: ignore
commands: commands:
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Analysing..." - echo "Analysing..."
@ -15,7 +16,6 @@ steps:
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Testing..." - echo "Testing..."
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- ./gradlew test -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
environment: environment:
SONAR_HOST_URL: SONAR_HOST_URL:
from_secret: sonarScannerHostUrl from_secret: sonarScannerHostUrl

View File

@ -30,7 +30,7 @@ fun gitVersion(): String {
println("Tag found on current commit") println("Tag found on current commit")
execWithOutput("git -C ../ describe --contains HEAD") execWithOutput("git -C ../ describe --contains HEAD")
} }
return process.replace("^0", "").replace("'", "").substring(1).replace("\\.", "").trim() return process.replace("'", "").substring(1).replace("\\.", "").trim()
} }
fun versionCodeFromGit(): Int { fun versionCodeFromGit(): Int {
@ -129,6 +129,7 @@ dependencies {
implementation("androidx.recyclerview:recyclerview:1.3.0-alpha01") implementation("androidx.recyclerview:recyclerview:1.3.0-alpha01")
implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02") implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02")
implementation("androidx.browser:browser:1.4.0")
implementation("androidx.cardview:cardview:1.0.0") implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.annotation:annotation:1.3.0") implementation("androidx.annotation:annotation:1.3.0")
implementation("androidx.work:work-runtime-ktx:2.7.1") implementation("androidx.work:work-runtime-ktx:2.7.1")
@ -166,6 +167,8 @@ dependencies {
implementation("com.mikepenz:materialdrawer:8.4.5") implementation("com.mikepenz:materialdrawer:8.4.5")
// Themes // Themes
implementation("com.52inc:scoops:1.0.0")
implementation("com.jaredrummler:colorpicker:1.1.0")
implementation("com.github.rubensousa:floatingtoolbar:1.5.1") implementation("com.github.rubensousa:floatingtoolbar:1.5.1")
// Pager // Pager

View File

@ -7,10 +7,13 @@ import android.widget.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.ftinc.scoop.Scoop
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -23,6 +26,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
private var mSpoutsValue: String? = null private var mSpoutsValue: String? = null
private lateinit var appColors: AppColors
private lateinit var binding: ActivityAddSourceBinding private lateinit var binding: ActivityAddSourceBinding
override val di by closestDI() override val di by closestDI()
@ -30,18 +34,43 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
private val appSettingsService : AppSettingsService by instance() private val appSettingsService : AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@AddSourceActivity)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityAddSourceBinding.inflate(layoutInflater) binding = ActivityAddSourceBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
setContentView(view) setContentView(view)
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
val drawable = binding.nameInput.background
drawable.setTint(appColors.colorAccent)
// TODO: clean
binding.nameInput.background = drawable
val drawable1 = binding.sourceUri.background
drawable1.setTint(appColors.colorAccent)
binding.sourceUri.background = drawable1
val drawable2 = binding.tags.background
drawable2.setTint(appColors.colorAccent)
binding.tags.background = drawable2
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
maybeGetDetailsFromIntentSharing(intent, binding.sourceUri, binding.nameInput) maybeGetDetailsFromIntentSharing(intent, binding.sourceUri, binding.nameInput)
binding.saveBtn.setTextColor(appColors.colorAccent)
binding.saveBtn.setOnClickListener { binding.saveBtn.setOnClickListener {
handleSaveSource( handleSaveSource(
binding.tags, binding.tags,
@ -55,7 +84,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
super.onResume() super.onResume()
val baseUrl = appSettingsService.getBaseUrl() val baseUrl = appSettingsService.getBaseUrl()
if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid()) { if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid(this@AddSourceActivity)) {
mustLoginToAddSource() mustLoginToAddSource()
} else { } else {
handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer) handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)

View File

@ -15,9 +15,6 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.view.doOnNextLayout import androidx.core.view.doOnNextLayout
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
@ -32,8 +29,11 @@ import bou.amine.apps.readerforselfossv2.android.adapters.ItemsAdapter
import bou.amine.apps.readerforselfossv2.android.background.LoadingWorker import bou.amine.apps.readerforselfossv2.android.background.LoadingWorker
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
@ -43,6 +43,7 @@ import com.ashokvarma.bottomnavigation.BottomNavigationItem
import com.ashokvarma.bottomnavigation.TextBadgeItem import com.ashokvarma.bottomnavigation.TextBadgeItem
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.ftinc.scoop.Scoop
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.materialdrawer.holder.BadgeStyle import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ColorHolder import com.mikepenz.materialdrawer.holder.ColorHolder
@ -79,6 +80,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
private lateinit var tabNewBadge: TextBadgeItem private lateinit var tabNewBadge: TextBadgeItem
private lateinit var tabArchiveBadge: TextBadgeItem private lateinit var tabArchiveBadge: TextBadgeItem
private lateinit var tabStarredBadge: TextBadgeItem private lateinit var tabStarredBadge: TextBadgeItem
private lateinit var customTabActivityHelper: CustomTabActivityHelper
private lateinit var appColors: AppColors
private var offset: Int = 0 private var offset: Int = 0
private var firstVisible: Int = 0 private var firstVisible: Int = 0
private lateinit var recyclerViewScrollListener: RecyclerView.OnScrollListener private lateinit var recyclerViewScrollListener: RecyclerView.OnScrollListener
@ -100,7 +103,16 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
data class DrawerData(val tags: List<SelfossModel.Tag>?, val sources: List<SelfossModel.Source>?) data class DrawerData(val tags: List<SelfossModel.Tag>?, val sources: List<SelfossModel.Source>?)
override fun onStart() {
super.onStart()
customTabActivityHelper.bindCustomTabsService(this)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// Add appcolors to DI
appColors = AppColors(this@HomeActivity)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater) binding = ActivityHomeBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
@ -114,6 +126,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
setContentView(view) setContentView(view)
handleThemeBinding()
setSupportActionBar(binding.toolBar) setSupportActionBar(binding.toolBar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true) supportActionBar?.setHomeButtonEnabled(true)
@ -121,16 +135,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
binding.drawerContainer.addDrawerListener(mDrawerToggle) binding.drawerContainer.addDrawerListener(mDrawerToggle)
mDrawerToggle.syncState() mDrawerToggle.syncState()
customTabActivityHelper = CustomTabActivityHelper()
handleBottomBar() handleBottomBar()
initDrawer() initDrawer()
handleSwipeRefreshLayout() handleSwipeRefreshLayout()
getElementsAccordingToTab()
if (appSettingsService.isItemCachingEnabled()) {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
repository.tryToCacheItemsAndGetNewOnes() repository.tryToCacheItemsAndGetNewOnes()
}
} }
} }
@ -217,24 +233,27 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
tabNewBadge = TextBadgeItem() tabNewBadge = TextBadgeItem()
.setText("") .setText("")
.setHideOnSelect(false).hide(false) .setHideOnSelect(false).hide(false)
.setBackgroundColor(appColors.colorPrimary)
tabArchiveBadge = TextBadgeItem() tabArchiveBadge = TextBadgeItem()
.setText("") .setText("")
.setHideOnSelect(false).hide(false) .setHideOnSelect(false).hide(false)
.setBackgroundColor(appColors.colorPrimary)
tabStarredBadge = TextBadgeItem() tabStarredBadge = TextBadgeItem()
.setText("") .setText("")
.setHideOnSelect(false).hide(false) .setHideOnSelect(false).hide(false)
.setBackgroundColor(appColors.colorPrimary)
val tabNew = val tabNew =
BottomNavigationItem( BottomNavigationItem(
R.drawable.ic_tab_fiber_new_black_24dp, R.drawable.ic_tab_fiber_new_black_24dp,
getString(R.string.tab_new) getString(R.string.tab_new)
) ).setActiveColor(appColors.colorAccent)
.setBadgeItem(tabNewBadge) .setBadgeItem(tabNewBadge)
val tabArchive = val tabArchive =
BottomNavigationItem( BottomNavigationItem(
R.drawable.ic_tab_archive_black_24dp, R.drawable.ic_tab_archive_black_24dp,
getString(R.string.tab_read) getString(R.string.tab_read)
) ).setActiveColor(appColors.colorAccentDark)
.setBadgeItem(tabArchiveBadge) .setBadgeItem(tabArchiveBadge)
val tabStarred = val tabStarred =
BottomNavigationItem( BottomNavigationItem(
@ -250,6 +269,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
.setFirstSelectedPosition(0) .setFirstSelectedPosition(0)
.initialise() .initialise()
binding.bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING) binding.bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
binding.bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
if (fromTabShortcut) { if (fromTabShortcut) {
binding.bottomBar.selectTab(elementsShown.position - 1) binding.bottomBar.selectTab(elementsShown.position - 1)
@ -259,8 +279,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
appColors = AppColors(this@HomeActivity)
handleDrawerItems() handleDrawerItems()
handleThemeUpdate()
reloadLayoutManager() reloadLayoutManager()
if (appSettingsService.isInfiniteLoadingEnabled()) { if (appSettingsService.isInfiniteLoadingEnabled()) {
@ -280,6 +304,25 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
getElementsAccordingToTab() getElementsAccordingToTab()
} }
override fun onStop() {
super.onStop()
customTabActivityHelper.unbindCustomTabsService(this)
}
private fun handleThemeBinding() {
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar)
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
}
private fun handleThemeUpdate() {
val scoop = Scoop.getInstance()
scoop.update(Toppings.PRIMARY.value, appColors.colorPrimary)
scoop.update(Toppings.PRIMARY_DARK.value, appColors.colorPrimaryDark)
}
private fun initDrawer() { private fun initDrawer() {
DrawerImageLoader.init(object : AbstractDrawerImageLoader() { DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) { override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
@ -355,7 +398,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
val drawerData = DrawerData(repository.getDBTags().map { it.toView() }, val drawerData = DrawerData(repository.getDBTags().map { it.toView() },
repository.getDBSources().map { it.toView() }) repository.getDBSources().map { it.toView() })
runOnUiThread { runOnUiThread {
// TODO: All this logic should be handled by the repository, simplify and remove direct DB access
// Only refresh if there is no data in the DB, or if the `UpdateSources` setting is enabled // Only refresh if there is no data in the DB, or if the `UpdateSources` setting is enabled
if (drawerData.sources?.isEmpty() == true || appSettingsService.isUpdateSourcesEnabled()) { if (drawerData.sources?.isEmpty() == true || appSettingsService.isUpdateSourcesEnabled()) {
drawerApiCalls(drawerData) drawerApiCalls(drawerData)
@ -465,7 +507,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
val gdColor = try { val gdColor = try {
Color.parseColor(it.color) Color.parseColor(it.color)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
resources.getColor(R.color.colorPrimary) appColors.colorPrimary
} }
gd.setColor(gdColor) gd.setColor(gdColor)
gd.shape = GradientDrawable.RECTANGLE gd.shape = GradientDrawable.RECTANGLE
@ -479,7 +521,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
iconDrawable = gd iconDrawable = gd
badgeStyle = BadgeStyle().apply { badgeStyle = BadgeStyle().apply {
textColor = ColorHolder.fromColor(Color.WHITE) textColor = ColorHolder.fromColor(Color.WHITE)
color = ColorHolder.fromColor(resources.getColor(R.color.colorAccent)) color = ColorHolder.fromColor(appColors.colorAccent)
} }
onDrawerItemClickListener = { _, _, _ -> onDrawerItemClickListener = { _, _, _ ->
repository.tagFilter = it repository.tagFilter = it
@ -702,6 +744,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
ItemCardAdapter( ItemCardAdapter(
this, this,
items, items,
customTabActivityHelper,
appColors,
) { ) {
updateItems(it) updateItems(it)
} }
@ -710,6 +754,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
ItemListAdapter( ItemListAdapter(
this, this,
items, items,
customTabActivityHelper,
appColors,
) { ) {
updateItems(it) updateItems(it)
} }

View File

@ -12,8 +12,8 @@ import android.view.inputmethod.EditorInfo
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
@ -30,6 +30,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
private var inValidCount: Int = 0 private var inValidCount: Int = 0
private var isWithLogin = false private var isWithLogin = false
private lateinit var appColors: AppColors
private lateinit var binding: ActivityLoginBinding private lateinit var binding: ActivityLoginBinding
override val di by closestDI() override val di by closestDI()
@ -37,9 +38,9 @@ class LoginActivity : AppCompatActivity(), DIAware {
private val appSettingsService : AppSettingsService by instance() private val appSettingsService : AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) appColors = AppColors(this@LoginActivity)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater) binding = ActivityLoginBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
@ -121,7 +122,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
var cancel = false var cancel = false
var focusView: View? = null var focusView: View? = null
if (url.isBaseUrlInvalid()) { if (url.isBaseUrlInvalid(this@LoginActivity)) {
binding.urlView.error = getString(R.string.login_url_problem) binding.urlView.error = getString(R.string.login_url_problem)
focusView = binding.urlView focusView = binding.urlView
cancel = true cancel = true
@ -163,6 +164,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val result = repository.login() val result = repository.login()
if (result) { if (result) {
repository.updateApiVersion()
goToMain() goToMain()
} else { } else {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {

View File

@ -21,6 +21,7 @@ import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.ftinc.scoop.Scoop
import com.github.ln_12.library.ConnectivityStatus import com.github.ln_12.library.ConnectivityStatus
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader
@ -53,6 +54,8 @@ class MyApp : MultiDexApplication(), DIAware {
initDrawerImageLoader() initDrawerImageLoader()
initTheme()
tryToHandleBug() tryToHandleBug()
handleNotificationChannels() handleNotificationChannels()
@ -114,6 +117,14 @@ class MyApp : MultiDexApplication(), DIAware {
}) })
} }
private fun initTheme() {
Scoop.waffleCone()
.addFlavor(getString(R.string.default_theme), R.style.NoBar, true)
.addFlavor(getString(R.string.default_dark_theme), R.style.NoBarDark, false)
.setSharedPreferences(PreferenceManager.getDefaultSharedPreferences(this))
.initialize()
}
private fun tryToHandleBug() { private fun tryToHandleBug() {
val oldHandler = Thread.getDefaultUncaughtExceptionHandler() val oldHandler = Thread.getDefaultUncaughtExceptionHandler()

View File

@ -12,9 +12,12 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityReaderBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivityReaderBinding
import bou.amine.apps.readerforselfossv2.android.fragments.ArticleFragment import bou.amine.apps.readerforselfossv2.android.fragments.ArticleFragment
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.ftinc.scoop.Scoop
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -25,6 +28,7 @@ import org.kodein.di.instance
class ReaderActivity : AppCompatActivity(), DIAware { class ReaderActivity : AppCompatActivity(), DIAware {
private var currentItem: Int = 0 private var currentItem: Int = 0
private lateinit var appColors: AppColors
private lateinit var toolbarMenu: Menu private lateinit var toolbarMenu: Menu
@ -52,11 +56,16 @@ class ReaderActivity : AppCompatActivity(), DIAware {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
appColors = AppColors(this)
binding = ActivityReaderBinding.inflate(layoutInflater) binding = ActivityReaderBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
setContentView(view) setContentView(view)
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar)
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
setSupportActionBar(binding.toolBar) setSupportActionBar(binding.toolBar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)

View File

@ -8,8 +8,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import com.ftinc.scoop.Scoop
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -19,15 +22,21 @@ import org.kodein.di.instance
class SourcesActivity : AppCompatActivity(), DIAware { class SourcesActivity : AppCompatActivity(), DIAware {
private lateinit var appColors: AppColors
private lateinit var binding: ActivitySourcesBinding private lateinit var binding: ActivitySourcesBinding
override val di by closestDI() override val di by closestDI()
private val repository : Repository by instance() private val repository : Repository by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@SourcesActivity)
binding = ActivitySourcesBinding.inflate(layoutInflater) binding = ActivitySourcesBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(view) setContentView(view)
@ -36,8 +45,8 @@ class SourcesActivity : AppCompatActivity(), DIAware {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark) binding.fab.rippleColor = appColors.colorAccentDark
binding.fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent)) binding.fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
} }
override fun onStop() { override fun onStop() {

View File

@ -10,7 +10,9 @@ import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.utils.* import bou.amine.apps.readerforselfossv2.android.utils.*
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
@ -32,6 +34,8 @@ import org.kodein.di.instance
class ItemCardAdapter( class ItemCardAdapter(
override val app: Activity, override val app: Activity,
override var items: ArrayList<SelfossModel.Item>, override var items: ArrayList<SelfossModel.Item>,
private val helper: CustomTabActivityHelper,
override val appColors: AppColors,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() { ) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
private val c: Context = app.baseContext private val c: Context = app.baseContext
@ -40,8 +44,8 @@ class ItemCardAdapter(
c.resources.getDimension(R.dimen.card_image_max_height).toInt() c.resources.getDimension(R.dimen.card_image_max_height).toInt()
override val di: DI by closestDI(app) override val di: DI by closestDI(app)
override val repository: Repository by instance() override val repository : Repository by instance()
override val appSettingsService: AppSettingsService by instance() override val appSettingsService : AppSettingsService by instance()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
@ -57,7 +61,7 @@ class ItemCardAdapter(
binding.title.setOnTouchListener(LinkOnTouchListener()) binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent)) binding.title.setLinkTextColor(appColors.colorAccent)
binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils) binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils)
@ -79,10 +83,10 @@ class ItemCardAdapter(
val color = generator.getColor(itm.title.getHtmlDecoded()) val color = generator.getColor(itm.title.getHtmlDecoded())
val drawable = val drawable =
TextDrawable TextDrawable
.builder() .builder()
.round() .round()
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color) .build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
binding.sourceImage.setImageDrawable(drawable) binding.sourceImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage) c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage)
@ -97,7 +101,7 @@ class ItemCardAdapter(
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) { inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
init { init {
handleClickListeners() handleClickListeners()
handleLinkOpening() handleCustomTabActions()
} }
private fun handleClickListeners() { private fun handleClickListeners() {
@ -107,34 +111,41 @@ class ItemCardAdapter(
if (item.starred) { if (item.starred) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.unstarr(item) repository.unstarr(item)
// TODO: Handle failure
} }
item.starred = false item.starred = false
binding.favButton.isSelected = false binding.favButton.isSelected = false
} else { } else {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.starr(item) repository.starr(item)
// TODO: Handle failure
} }
item.starred = true item.starred = true
binding.favButton.isSelected = true binding.favButton.isSelected = true
} }
} }
binding.shareBtn.setOnClickListener { binding.shareBtn.setOnClickListener {
val item = items[bindingAdapterPosition] val item = items[bindingAdapterPosition]
c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded()) c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
}
binding.browserBtn.setOnClickListener {
c.openInBrowserAsNewTask(items[bindingAdapterPosition])
}
} }
binding.browserBtn.setOnClickListener { private fun handleCustomTabActions() {
c.openInBrowserAsNewTask(items[bindingAdapterPosition]) val customTabsIntent = c.buildCustomTabsIntent()
} helper.bindCustomTabsService(app)
}
private fun handleLinkOpening() {
binding.root.setOnClickListener { binding.root.setOnClickListener {
c.openItemUrl( c.openItemUrl(
items, items,
bindingAdapterPosition, bindingAdapterPosition,
items[bindingAdapterPosition].getLinkDecoded(), items[bindingAdapterPosition].getLinkDecoded(),
customTabsIntent,
appSettingsService.isInternalBrowserEnabled(),
appSettingsService.isArticleViewerEnabled(), appSettingsService.isArticleViewerEnabled(),
app app
) )

View File

@ -5,10 +5,12 @@ import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
import bou.amine.apps.readerforselfossv2.android.utils.buildCustomTabsIntent
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
@ -27,6 +29,8 @@ import org.kodein.di.instance
class ItemListAdapter( class ItemListAdapter(
override val app: Activity, override val app: Activity,
override var items: ArrayList<SelfossModel.Item>, override var items: ArrayList<SelfossModel.Item>,
private val helper: CustomTabActivityHelper,
override val appColors: AppColors,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
) : ItemsAdapter<ItemListAdapter.ViewHolder>() { ) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
@ -49,7 +53,7 @@ class ItemListAdapter(
binding.title.setOnTouchListener(LinkOnTouchListener()) binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent)) binding.title.setLinkTextColor(appColors.colorAccent)
binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils) binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils)
@ -79,15 +83,20 @@ class ItemListAdapter(
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) { inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
init { init {
handleLinkOpening() handleCustomTabActions()
} }
private fun handleLinkOpening() { private fun handleCustomTabActions() {
val customTabsIntent = c.buildCustomTabsIntent()
helper.bindCustomTabsService(app)
binding.root.setOnClickListener { binding.root.setOnClickListener {
c.openItemUrl( c.openItemUrl(
items, items,
bindingAdapterPosition, bindingAdapterPosition,
items[bindingAdapterPosition].getLinkDecoded(), items[bindingAdapterPosition].getLinkDecoded(),
customTabsIntent,
appSettingsService.isInternalBrowserEnabled(),
appSettingsService.isArticleViewerEnabled(), appSettingsService.isArticleViewerEnabled(),
app app
) )

View File

@ -5,6 +5,7 @@ import android.graphics.Color
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
@ -20,6 +21,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
abstract val repository: Repository abstract val repository: Repository
abstract val appSettingsService: AppSettingsService abstract val appSettingsService: AppSettingsService
abstract val app: Activity abstract val app: Activity
abstract val appColors: AppColors
abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
fun updateAllItems(items: ArrayList<SelfossModel.Item>) { fun updateAllItems(items: ArrayList<SelfossModel.Item>) {
@ -28,7 +30,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
updateItems(this.items) updateItems(this.items)
} }
private fun unmarkSnackbar(position: Int) { private fun unmarkSnackbar(i: SelfossModel.Item, position: Int) {
val s = Snackbar val s = Snackbar
.make( .make(
app.findViewById(R.id.coordLayout), app.findViewById(R.id.coordLayout),
@ -85,7 +87,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
notifyItemChanged(position) notifyItemChanged(position)
} }
if (showSnackbar) { if (showSnackbar) {
unmarkSnackbar(position) unmarkSnackbar(i, position)
} }
} }

View File

@ -52,13 +52,11 @@ override fun doWork(): Result {
repository.handleDBActions() repository.handleDBActions()
val apiItems = repository.tryToCacheItemsAndGetNewOnes()
if (appSettingsService.isNotifyNewItemsEnabled()) { if (appSettingsService.isNotifyNewItemsEnabled()) {
launch { launch {
handleNewItemsNotification(apiItems, notificationManager) handleNewItemsNotification(repository.tryToCacheItemsAndGetNewOnes(), notificationManager)
} }
} }
apiItems.map { it.preloadImages(context) }
} }
} }
return Result.success() return Result.success()
@ -68,7 +66,6 @@ override fun doWork(): Result {
newItems: List<SelfossModel.Item>?, newItems: List<SelfossModel.Item>?,
notificationManager: NotificationManager notificationManager: NotificationManager
) { ) {
// TODO: Check if this coroutine is actually required
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val apiItems = newItems.orEmpty() val apiItems = newItems.orEmpty()
@ -105,6 +102,7 @@ override fun doWork(): Result {
notificationManager.notify(2, newItemsNotification.build()) notificationManager.notify(2, newItemsNotification.build())
} }
} }
apiItems.map { it.preloadImages(context) }
Timer("", false).schedule(4000) { Timer("", false).schedule(4000) {
notificationManager.cancel(1) notificationManager.cancel(1)
} }

View File

@ -8,7 +8,6 @@ import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue
import android.view.* import android.view.*
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebSettings import android.webkit.WebSettings
@ -16,6 +15,7 @@ import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -27,8 +27,12 @@ import bou.amine.apps.readerforselfossv2.android.databinding.FragmentArticleBind
import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem
import bou.amine.apps.readerforselfossv2.android.model.toModel import bou.amine.apps.readerforselfossv2.android.model.toModel
import bou.amine.apps.readerforselfossv2.android.model.toParcelable import bou.amine.apps.readerforselfossv2.android.model.toParcelable
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.utils.buildCustomTabsIntent
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInternalBrowser
import bou.amine.apps.readerforselfossv2.android.utils.shareLink import bou.amine.apps.readerforselfossv2.android.utils.shareLink
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
@ -57,10 +61,10 @@ import java.net.URL
import java.util.* import java.util.*
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
class ArticleFragment : Fragment(), DIAware { class ArticleFragment : Fragment(), DIAware {
private var fontSize: Int = 16 private var fontSize: Int = 16
private lateinit var item: SelfossModel.Item private lateinit var item: SelfossModel.Item
private var mCustomTabActivityHelper: CustomTabActivityHelper? = null
private lateinit var url: String private lateinit var url: String
private lateinit var contentText: String private lateinit var contentText: String
private lateinit var contentSource: String private lateinit var contentSource: String
@ -68,6 +72,7 @@ class ArticleFragment : Fragment(), DIAware {
private lateinit var contentTitle: String private lateinit var contentTitle: String
private lateinit var allImages : ArrayList<String> private lateinit var allImages : ArrayList<String>
private lateinit var fab: FloatingActionButton private lateinit var fab: FloatingActionButton
private lateinit var appColors: AppColors
private lateinit var textAlignment: String private lateinit var textAlignment: String
private var _binding: FragmentArticleBinding? = null private var _binding: FragmentArticleBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -81,7 +86,16 @@ class ArticleFragment : Fragment(), DIAware {
private var font = "" private var font = ""
private var staticBar = false private var staticBar = false
override fun onStop() {
super.onStop()
if (mCustomTabActivityHelper != null) {
mCustomTabActivityHelper!!.unbindCustomTabsService(activity)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(requireActivity())
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val pi: ParecelableItem = requireArguments().getParcelable(ARG_ITEMS)!! val pi: ParecelableItem = requireArguments().getParcelable(ARG_ITEMS)!!
@ -122,19 +136,25 @@ class ArticleFragment : Fragment(), DIAware {
fab = binding.fab fab = binding.fab
fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent)) fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
fab.rippleColor = resources.getColor(R.color.colorAccentDark) fab.rippleColor = appColors.colorAccentDark
val floatingToolbar: FloatingToolbar = binding.floatingToolbar val floatingToolbar: FloatingToolbar = binding.floatingToolbar
floatingToolbar.attachFab(fab) floatingToolbar.attachFab(fab)
floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent)) floatingToolbar.background = ColorDrawable(appColors.colorAccent)
val customTabsIntent = requireActivity().buildCustomTabsIntent()
mCustomTabActivityHelper = CustomTabActivityHelper()
mCustomTabActivityHelper!!.bindCustomTabsService(activity)
floatingToolbar.setClickListener( floatingToolbar.setClickListener(
object : FloatingToolbar.ItemClickListener { object : FloatingToolbar.ItemClickListener {
override fun onItemClick(item: MenuItem) { override fun onItemClick(item: MenuItem) {
when (item.itemId) { when (item.itemId) {
R.id.more_action -> getContentFromMercury(customTabsIntent)
R.id.share_action -> requireActivity().shareLink(url, contentTitle) R.id.share_action -> requireActivity().shareLink(url, contentTitle)
R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item) R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
R.id.unread_action -> if (context != null) { R.id.unread_action -> if (context != null) {
@ -180,7 +200,7 @@ class ArticleFragment : Fragment(), DIAware {
} }
if (contentText.isEmptyOrNullOrNullString()) { if (contentText.isEmptyOrNullOrNullString()) {
getContentFromMercury() getContentFromMercury(customTabsIntent)
} else { } else {
binding.titleView.text = contentTitle binding.titleView.text = contentTitle
if (typeface != null) { if (typeface != null) {
@ -246,7 +266,7 @@ class ArticleFragment : Fragment(), DIAware {
} }
} }
private fun getContentFromMercury() { private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
if (repository.isNetworkAvailable()) { if (repository.isNetworkAvailable()) {
binding.progressBar.visibility = View.VISIBLE binding.progressBar.visibility = View.VISIBLE
val parser = MercuryApi() val parser = MercuryApi()
@ -313,7 +333,7 @@ class ArticleFragment : Fragment(), DIAware {
} }
} else { } else {
try { try {
openInBrowserAfterFailing() openInBrowserAfterFailing(customTabsIntent)
} catch (e: Exception) { } catch (e: Exception) {
if (context != null) { if (context != null) {
} }
@ -328,13 +348,14 @@ class ArticleFragment : Fragment(), DIAware {
override fun onFailure( override fun onFailure(
call: Call<ParsedContent>, call: Call<ParsedContent>,
t: Throwable t: Throwable
) = openInBrowserAfterFailing() ) = openInBrowserAfterFailing(customTabsIntent)
} }
) )
} }
} }
private fun htmlToWebview() { private fun htmlToWebview() {
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily) val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs) val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
@ -343,11 +364,12 @@ class ArticleFragment : Fragment(), DIAware {
binding.webcontent.settings.standardFontFamily = a.getString(0) binding.webcontent.settings.standardFontFamily = a.getString(0)
binding.webcontent.visibility = View.VISIBLE binding.webcontent.visibility = View.VISIBLE
val colorOnSurface = TypedValue() // TODO: Set the color strings programmatically
requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true) val (stringTextColor, stringBackgroundColor) = if (appColors.isDarkTheme) {
Pair("#FFFFFF", "#303030")
val colorSurface = TypedValue() } else {
requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true) Pair("#212121", "#FAFAFA")
}
binding.webcontent.settings.useWideViewPort = true binding.webcontent.settings.useWideViewPort = true
binding.webcontent.settings.loadWithOverviewMode = true binding.webcontent.settings.loadWithOverviewMode = true
@ -436,10 +458,10 @@ class ArticleFragment : Fragment(), DIAware {
| max-width: 100%; | max-width: 100%;
| } | }
| a { | a {
| color: ${String.format("#%06X", 0xFFFFFF and resources.getColor(R.color.colorAccent))} !important; | color: $stringColor !important;
| } | }
| *:not(a) { | *:not(a) {
| color: ${String.format("#%06X", 0xFFFFFF and colorOnSurface.data)}; | color: $stringTextColor;
| } | }
| * { | * {
| font-size: ${fontSize}px; | font-size: ${fontSize}px;
@ -447,11 +469,11 @@ class ArticleFragment : Fragment(), DIAware {
| word-break: break-word; | word-break: break-word;
| overflow:hidden; | overflow:hidden;
| line-height: 1.5em; | line-height: 1.5em;
| background-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)}; | background-color: $stringBackgroundColor;
| } | }
| body, html { | body, html {
| background-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)} !important; | background-color: $stringBackgroundColor !important;
| border-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)} !important; | border-color: $stringBackgroundColor !important;
| padding: 0 !important; | padding: 0 !important;
| margin: 0 !important; | margin: 0 !important;
| } | }
@ -461,7 +483,7 @@ class ArticleFragment : Fragment(), DIAware {
| pre, code { | pre, code {
| white-space: pre-wrap; | white-space: pre-wrap;
| width:100%; | width:100%;
| background-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)}; | background-color: $stringBackgroundColor;
| } | }
| </style> | </style>
| $fontLinkAndStyle | $fontLinkAndStyle
@ -485,9 +507,13 @@ class ArticleFragment : Fragment(), DIAware {
binding.nestedScrollView.smoothScrollBy(0, -height/2) binding.nestedScrollView.smoothScrollBy(0, -height/2)
} }
private fun openInBrowserAfterFailing() { private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
binding.progressBar.visibility = View.GONE binding.progressBar.visibility = View.GONE
requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item) requireActivity().openItemUrlInternalBrowser(
url,
customTabsIntent,
requireActivity()
)
} }
companion object { companion object {

View File

@ -7,16 +7,22 @@ import android.text.Editable
import android.text.InputFilter import android.text.InputFilter
import android.text.InputType import android.text.InputType
import android.text.TextWatcher import android.text.TextWatcher
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.ftinc.scoop.Scoop
private const val TITLE_TAG = "settingsActivityTitle" private const val TITLE_TAG = "settingsActivityTitle"
@ -25,8 +31,15 @@ class SettingsActivity : AppCompatActivity(),
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", false)) {
setTheme(R.style.NoBarDark)
}
val binding = ActivitySettingsBinding.inflate(layoutInflater) val binding = ActivitySettingsBinding.inflate(layoutInflater)
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
setContentView(binding.root) setContentView(binding.root)
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager supportFragmentManager
@ -156,11 +169,21 @@ class SettingsActivity : AppCompatActivity(),
class ThemePreferenceFragment : PreferenceFragmentCompat() { class ThemePreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.pref_theme, rootKey) setPreferencesFromResource(R.xml.pref_theme, rootKey)
setHasOptionsMenu(true)
}
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ super.onCreateOptionsMenu(menu, inflater)
true inflater.inflate(R.menu.settings_theme, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == R.id.clear) {
AppColors.resetColors()
requireActivity().recreate()
} }
return super.onOptionsItemSelected(item)
} }
} }

View File

@ -0,0 +1,72 @@
package bou.amine.apps.readerforselfossv2.android.themes
import android.app.Activity
import androidx.annotation.ColorInt
import bou.amine.apps.readerforselfossv2.android.R
import com.russhwolf.settings.Settings
class AppColors(a: Activity) {
@ColorInt val colorPrimary: Int
@ColorInt val colorPrimaryDark: Int
@ColorInt val colorAccent: Int
@ColorInt val colorAccentDark: Int
@ColorInt val colorBackground: Int
@ColorInt val textColor: Int
val isDarkTheme: Boolean
init {
val settings = Settings()
colorPrimary =
settings.getInt(
"color_primary",
a.resources.getColor(R.color.colorPrimary)
)
colorPrimaryDark =
settings.getInt(
"color_primary_dark",
a.resources.getColor(R.color.colorPrimaryDark)
)
colorAccent =
settings.getInt(
"color_accent",
a.resources.getColor(R.color.colorAccent)
)
colorAccentDark =
settings.getInt(
"color_accent_dark",
a.resources.getColor(R.color.colorAccentDark)
)
isDarkTheme =
settings.getBoolean(
"dark_theme",
false
)
colorBackground = if (isDarkTheme) {
a.setTheme(R.style.NoBarDark)
a.resources.getColor(R.color.darkBackground)
} else {
a.setTheme(R.style.NoBar)
a.resources.getColor(R.color.grey_50)
}
textColor = if (isDarkTheme) {
a.resources.getColor(R.color.white)
} else {
a.resources.getColor(R.color.grey_900)
}
}
companion object {
fun resetColors() {
val settings = Settings()
settings.remove("color_primary")
settings.remove("color_primary_dark")
settings.remove("color_accent")
settings.remove("color_accent_dark")
settings.remove("dark_theme")
}
}
}

View File

@ -0,0 +1,8 @@
package bou.amine.apps.readerforselfossv2.android.themes
enum class Toppings(val value: Int) {
PRIMARY(1),
PRIMARY_DARK(2),
ACCENT(3),
ACCENT_DARK(4)
}

View File

@ -15,16 +15,109 @@ import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.browser.customtabs.CustomTabsIntent
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.ReaderActivity import bou.amine.apps.readerforselfossv2.android.ReaderActivity
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
val actionIntent = Intent(Intent.ACTION_SEND)
actionIntent.type = "text/plain"
val pflags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE
} else {
0
}
val createPendingShareIntent: PendingIntent = PendingIntent.getActivity(
this,
0,
actionIntent,
pflags
)
val intentBuilder = CustomTabsIntent.Builder()
// TODO: change to primary when it's possible to customize custom tabs title color
//intentBuilder.setToolbarColor(c.getResources().getColor(R.color.colorPrimary));
intentBuilder.setToolbarColor(resources.getColor(R.color.colorAccentDark))
intentBuilder.setShowTitle(true)
intentBuilder.setStartAnimations(
this,
R.anim.slide_in_right,
R.anim.slide_out_left
)
intentBuilder.setExitAnimations(
this,
android.R.anim.slide_in_left,
android.R.anim.slide_out_right
)
val closeicon = BitmapFactory.decodeResource(resources, R.drawable.ic_close_white_24dp)
intentBuilder.setCloseButtonIcon(closeicon)
val shareLabel = this.getString(R.string.label_share)
val icon = BitmapFactory.decodeResource(
resources,
R.drawable.ic_share_white_24dp
)
intentBuilder.setActionButton(icon, shareLabel, createPendingShareIntent)
return intentBuilder.build()
}
fun Context.openItemUrlInternally(
allItems: ArrayList<SelfossModel.Item>,
currentItem: Int,
linkDecoded: String,
customTabsIntent: CustomTabsIntent,
articleViewer: Boolean,
app: Activity
) {
if (articleViewer) {
ReaderActivity.allItems = allItems
val intent = Intent(this, ReaderActivity::class.java)
intent.putExtra("currentItem", currentItem)
app.startActivity(intent)
} else {
this.openItemUrlInternalBrowser(
linkDecoded,
customTabsIntent,
app)
}
}
fun Context.openItemUrlInternalBrowser(
linkDecoded: String,
customTabsIntent: CustomTabsIntent,
app: Activity
) {
try {
CustomTabActivityHelper.openCustomTab(
app,
customTabsIntent,
Uri.parse(linkDecoded)
) { _, uri ->
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
} catch (e: Exception) {
openInBrowser(linkDecoded, app)
}
}
fun Context.openItemUrl( fun Context.openItemUrl(
allItems: ArrayList<SelfossModel.Item>, allItems: ArrayList<SelfossModel.Item>,
currentItem: Int, currentItem: Int,
linkDecoded: String, linkDecoded: String,
customTabsIntent: CustomTabsIntent,
internalBrowser: Boolean,
articleViewer: Boolean, articleViewer: Boolean,
app: Activity app: Activity
) { ) {
@ -36,24 +129,41 @@ fun Context.openItemUrl(
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} else { } else {
if (articleViewer) { if (!internalBrowser) {
ReaderActivity.allItems = allItems openInBrowser(linkDecoded, app)
val intent = Intent(this, ReaderActivity::class.java) } else if (articleViewer) {
intent.putExtra("currentItem", currentItem) this.openItemUrlInternally(
app.startActivity(intent) allItems,
currentItem,
linkDecoded,
customTabsIntent,
articleViewer,
app
)
} else { } else {
val intent = Intent(Intent.ACTION_VIEW) this.openItemUrlInternalBrowser(
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK linkDecoded,
intent.data = Uri.parse(linkDecoded.toStringUriWithHttp()) customTabsIntent,
startActivity(intent) app
)
} }
} }
} }
private fun openInBrowser(linkDecoded: String, app: Activity) {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(linkDecoded)
try {
app.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(app.baseContext, e.message, Toast.LENGTH_LONG).show()
}
}
fun String.isUrlValid(): Boolean = fun String.isUrlValid(): Boolean =
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches() this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
fun String.isBaseUrlInvalid(): Boolean { fun String.isBaseUrlInvalid(ctx: Context): Boolean {
val baseUrl = this.toHttpUrlOrNull() val baseUrl = this.toHttpUrlOrNull()
var existsAndEndsWithSlash = false var existsAndEndsWithSlash = false
if (baseUrl != null) { if (baseUrl != null) {
@ -71,7 +181,7 @@ fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) {
startActivity(intent) startActivity(intent)
} }
class LinkOnTouchListener : View.OnTouchListener { class LinkOnTouchListener: View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean { override fun onTouch(v: View?, event: MotionEvent?): Boolean {
var ret = false var ret = false
val widget: TextView = v as TextView val widget: TextView = v as TextView
@ -81,8 +191,7 @@ class LinkOnTouchListener : View.OnTouchListener {
val action = event!!.action val action = event!!.action
if (action == MotionEvent.ACTION_UP || if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN action == MotionEvent.ACTION_DOWN) {
) {
var x: Float = event.x var x: Float = event.x
var y: Float = event.y var y: Float = event.y

View File

@ -0,0 +1,153 @@
package bou.amine.apps.readerforselfossv2.android.utils.customtabs;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import androidx.browser.customtabs.CustomTabsClient;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsServiceConnection;
import androidx.browser.customtabs.CustomTabsSession;
import java.util.List;
/**
* This is a helper class to manage the connection to the Custom Tabs Service.
*/
public class CustomTabActivityHelper implements ServiceConnectionCallback {
private CustomTabsSession mCustomTabsSession;
private CustomTabsClient mClient;
private CustomTabsServiceConnection mConnection;
private ConnectionCallback mConnectionCallback;
/**
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
*
* @param activity The host activity.
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
* @param uri the Uri to be opened.
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available.
*/
public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means theres no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (packageName == null) {
if (fallback != null) {
fallback.openUri(activity, uri);
}
} else {
customTabsIntent.intent.setPackage(packageName);
customTabsIntent.launchUrl(activity, uri);
}
}
/**
* Unbinds the Activity from the Custom Tabs Service.
*
* @param activity the activity that is connected to the service.
*/
public void unbindCustomTabsService(Activity activity) {
if (mConnection == null) return;
activity.unbindService(mConnection);
mClient = null;
mCustomTabsSession = null;
mConnection = null;
}
/**
* Creates or retrieves an exiting CustomTabsSession.
*
* @return a CustomTabsSession.
*/
public CustomTabsSession getSession() {
if (mClient == null) {
mCustomTabsSession = null;
} else if (mCustomTabsSession == null) {
mCustomTabsSession = mClient.newSession(null);
}
return mCustomTabsSession;
}
/**
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service.
*
* @param connectionCallback
*/
public void setConnectionCallback(ConnectionCallback connectionCallback) {
this.mConnectionCallback = connectionCallback;
}
/**
* Binds the Activity to the Custom Tabs Service.
*
* @param activity the activity to be binded to the service.
*/
public void bindCustomTabsService(Activity activity) {
if (mClient != null) return;
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
if (packageName == null) return;
mConnection = new ServiceConnection(this);
CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
}
/**
* @return true if call to mayLaunchUrl was accepted.
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
*/
public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) {
if (mClient == null) return false;
CustomTabsSession session = getSession();
return session != null && session.mayLaunchUrl(uri, extras, otherLikelyBundles);
}
@Override
public void onServiceConnected(CustomTabsClient client) {
mClient = client;
mClient.warmup(0L);
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
}
@Override
public void onServiceDisconnected() {
mClient = null;
mCustomTabsSession = null;
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
}
/**
* A Callback for when the service is connected or disconnected. Use those callbacks to
* handle UI changes when the service is connected or disconnected.
*/
public interface ConnectionCallback {
/**
* Called when the service is connected.
*/
void onCustomTabsConnected();
/**
* Called when the service is disconnected.
*/
void onCustomTabsDisconnected();
}
/**
* To be used as a fallback to open the Uri when Custom Tabs is not available.
*/
public interface CustomTabFallback {
/**
* @param activity The Activity that wants to open the Uri.
* @param uri The uri to be opened by the fallback.
*/
void openUri(Activity activity, Uri uri);
}
}

View File

@ -0,0 +1,129 @@
package bou.amine.apps.readerforselfossv2.android.utils.customtabs;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import androidx.browser.customtabs.CustomTabsService;
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.helpers.KeepAliveService;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("ALL")
class CustomTabsHelper {
private static final String TAG = "CustomTabsHelper";
private static final String STABLE_PACKAGE = "com.android.chrome";
private static final String BETA_PACKAGE = "com.chrome.beta";
private static final String DEV_PACKAGE = "com.chrome.dev";
private static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
private static final String EXTRA_CUSTOM_TABS_KEEP_ALIVE =
"android.support.customtabs.extra.KEEP_ALIVE";
private static String sPackageNameToUse;
private CustomTabsHelper() {
}
public static void addKeepAliveExtra(Context context, Intent intent) {
Intent keepAliveIntent = new Intent().setClassName(
context.getPackageName(), KeepAliveService.class.getCanonicalName());
intent.putExtra(EXTRA_CUSTOM_TABS_KEEP_ALIVE, keepAliveIntent);
}
/**
* Goes through all apps that handle VIEW intents and have a warmup service. Picks
* the one chosen by the user if there is one, otherwise makes a best effort to return a
* valid package name.
* <p>
* This is <strong>not</strong> threadsafe.
*
* @param context {@link Context} to use for accessing {@link PackageManager}.
* @return The package name recommended to use for connecting to custom tabs related components.
*/
public static String getPackageNameToUse(Context context) {
if (sPackageNameToUse != null) return sPackageNameToUse;
PackageManager pm = context.getPackageManager();
// Get default VIEW intent handler.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
String defaultViewHandlerPackageName = null;
if (defaultViewHandlerInfo != null) {
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
}
// Get all apps that can handle VIEW intents.
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
sPackageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
sPackageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
&& !hasSpecializedHandlerIntents(context, activityIntent)
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
sPackageNameToUse = defaultViewHandlerPackageName;
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
sPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
sPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
sPackageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
sPackageNameToUse = LOCAL_PACKAGE;
}
return sPackageNameToUse;
}
/**
* Used to check whether there is a specialized handler for a given intent.
*
* @param intent The intent to check with.
* @return Whether there is a specialized handler for the given intent.
*/
private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
try {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> handlers = pm.queryIntentActivities(
intent,
PackageManager.GET_RESOLVED_FILTER);
if (handlers == null || handlers.isEmpty()) {
return false;
}
for (ResolveInfo resolveInfo : handlers) {
IntentFilter filter = resolveInfo.filter;
if (filter == null) continue;
if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
if (resolveInfo.activityInfo == null) continue;
return true;
}
} catch (RuntimeException e) {
Log.e(TAG, "Runtime exception while getting specialized handlers");
}
return false;
}
/**
* @return All possible chrome package names that provide custom tabs feature.
*/
public static String[] getPackages() {
return new String[]{"", STABLE_PACKAGE, BETA_PACKAGE, DEV_PACKAGE, LOCAL_PACKAGE};
}
}

View File

@ -0,0 +1,33 @@
package bou.amine.apps.readerforselfossv2.android.utils.customtabs;
import android.content.ComponentName;
import androidx.browser.customtabs.CustomTabsClient;
import androidx.browser.customtabs.CustomTabsServiceConnection;
import java.lang.ref.WeakReference;
/**
* Implementation for the CustomTabsServiceConnection that avoids leaking the
* ServiceConnectionCallback
*/
public class ServiceConnection extends CustomTabsServiceConnection {
// A weak reference to the ServiceConnectionCallback to avoid leaking it.
private WeakReference<ServiceConnectionCallback> mConnectionCallback;
public ServiceConnection(ServiceConnectionCallback connectionCallback) {
mConnectionCallback = new WeakReference<>(connectionCallback);
}
@Override
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
if (connectionCallback != null) connectionCallback.onServiceConnected(client);
}
@Override
public void onServiceDisconnected(ComponentName name) {
ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
if (connectionCallback != null) connectionCallback.onServiceDisconnected();
}
}

View File

@ -0,0 +1,19 @@
package bou.amine.apps.readerforselfossv2.android.utils.customtabs;
import androidx.browser.customtabs.CustomTabsClient;
public interface ServiceConnectionCallback {
/**
* Called when the service is connected.
*
* @param client a CustomTabsClient
*/
void onServiceConnected(CustomTabsClient client);
/**
* Called when the service is disconnected.
*/
void onServiceDisconnected();
}

View File

@ -0,0 +1,15 @@
package bou.amine.apps.readerforselfossv2.android.utils.customtabs.helpers;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class KeepAliveService extends Service {
private static final Binder sBinder = new Binder();
@Override
public IBinder onBind(Intent intent) {
return sBinder;
}
}

View File

@ -1,11 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item> <item
<shape android:shape="rectangle" > android:drawable="@color/ic_launcher_background"/>
<solid android:color="?attr/colorSurface" />
</shape>
</item>
<item> <item>
<bitmap <bitmap

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M13,12h7v1.5h-7zM13,9.5h7L20,11h-7zM13,14.5h7L20,16h-7zM21,4L3,4c-1.1,0 -2,0.9 -2,2v13c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,6c0,-1.1 -0.9,-2 -2,-2zM21,19h-9L12,6h9v13z"/>
</vector>

View File

@ -14,12 +14,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
/> app:popupTheme="?attr/toolbarPopupTheme" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -107,7 +107,8 @@
app:layout_constraintTop_toBottomOf="@+id/tags" app:layout_constraintTop_toBottomOf="@+id/tags"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:layout_height="40dp"/> android:layout_height="40dp"
android:theme="@style/App.Spinner"/>
<Button <Button
android:text="@string/add_source_save" android:text="@string/add_source_save"
@ -115,7 +116,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/saveBtn" android:id="@+id/saveBtn"
android:elevation="5dp" android:elevation="5dp"
android:textColor="?android:textColorPrimary" android:textColor="?attr/colorAccent"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="16dp" android:layout_marginRight="16dp"

View File

@ -28,12 +28,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <androidx.appcompat.widget.Toolbar
android:id="@+id/toolBar" android:id="@+id/toolBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
/> app:popupTheme="?attr/toolbarPopupTheme" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -78,12 +78,10 @@
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.ashokvarma.bottomnavigation.BottomNavigationBar <com.ashokvarma.bottomnavigation.BottomNavigationBar
android:layout_gravity="bottom"
android:id="@+id/bottomBar" android:id="@+id/bottomBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="60dp"/>
android:layout_gravity="bottom"
app:bnbActiveColor="@color/colorAccent"
app:bnbBackgroundColor="?attr/bottomBarBackground" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView <com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView

View File

@ -16,8 +16,8 @@
android:id="@+id/toolBar" android:id="@+id/toolBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:popupTheme="?attr/toolbarPopupTheme"
/> app:theme="@style/ToolBarStyle" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -10,12 +10,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
/> app:popupTheme="?attr/toolbarPopupTheme" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<LinearLayout <LinearLayout

View File

@ -17,10 +17,8 @@
android:id="@+id/toolBar" android:id="@+id/toolBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme" app:popupTheme="?attr/toolbarPopupTheme"
app:theme="@style/ToolBarStyle" />
/>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -1,5 +1,4 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout" android:id="@+id/layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -9,11 +8,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
/> android:theme="@style/ToolBarStyle" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<FrameLayout <FrameLayout

View File

@ -10,12 +10,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
/> app:popupTheme="?attr/toolbarPopupTheme" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -16,7 +16,8 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
card_view:cardElevation="2dp" card_view:cardElevation="2dp"
card_view:cardUseCompatPadding="true" card_view:cardUseCompatPadding="true"
card_view:layout_constraintBottom_toBottomOf="parent"> card_view:layout_constraintBottom_toBottomOf="parent"
app:cardBackgroundColor="?cardBackgroundColor">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -66,7 +66,7 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:paddingBottom="48dp" android:paddingBottom="48dp"
android:background="?attr/webviewBackground" android:background="?android:colorBackground"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"

View File

@ -8,6 +8,12 @@
android:title="@string/unmark" android:title="@string/unmark"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/more_action"
android:icon="@drawable/ic_chrome_reader_mode_white_24dp"
android:title="@string/reader_action_more"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/open_action" android:id="@+id/open_action"
android:icon="@drawable/ic_open_in_browser_white_24dp" android:icon="@drawable/ic_open_in_browser_white_24dp"

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/clear"
android:icon="@drawable/ic_history_white_24dp"
android:title="@string/drawer_action_clear"
app:showAsAction="ifRoom" />
</menu>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Recompte d'articles no llegits"</string> <string name="switch_unread_count_title">"Recompte d'articles no llegits"</string>
<string name="display_all_counts_title">"Recompte d'articles llegits i preferits"</string> <string name="display_all_counts_title">"Recompte d'articles llegits i preferits"</string>
<string name="text_wrong_url">"Sembla que esteu utilitzant un URL no vàlid. Assegureu-vos que és correcte, i si el problema persisteix, poseu-vos en contacte amb mi (a través de l'enllaç de contacte que hi ha a la Botiga). Tingueu en compte que per utilitzar aquesta aplicació cal que també utilitzeu Selfoss. Si no, no podreu accedir a canals RSS."</string> <string name="text_wrong_url">"Sembla que esteu utilitzant un URL no vàlid. Assegureu-vos que és correcte, i si el problema persisteix, poseu-vos en contacte amb mi (a través de l'enllaç de contacte que hi ha a la Botiga). Tingueu en compte que per utilitzar aquesta aplicació cal que també utilitzeu Selfoss. Si no, no podreu accedir a canals RSS."</string>
<string name="pref_article_viewer_title">"Obre els enllaços dins de l'aplicació"</string> <string name="pref_general_internal_browser_title">"Obre els enllaços dins de l'aplicació"</string>
<string name="pref_article_viewer_on">"Els articles s'obriran dins de l'aplicació"</string> <string name="pref_general_internal_browser_on">"Els articles s'obriran dins de l'aplicació"</string>
<string name="pref_article_viewer_off">"Els articles s'obriran amb el navegador predeterminat"</string> <string name="pref_general_internal_browser_off">"Els articles s'obriran amb el navegador predeterminat"</string>
<string name="prefer_article_viewer_title">"Obre el visualitzador d'articles"</string>
<string name="prefer_article_viewer_on">"S'obrirà el visualitzador d'articles en lloc del navegador intern"</string>
<string name="prefer_article_viewer_off">"S'obrirà el navegador intern en lloc del visualitzador d'articles"</string>
<string name="pref_general_category_links">"Gestió d'enllaços"</string> <string name="pref_general_category_links">"Gestió d'enllaços"</string>
<string name="pref_general_category_displaying">"Visualització"</string> <string name="pref_general_category_displaying">"Visualització"</string>
<string name="pref_switch_card_view_on">"Els articles es mostraran com a targetes"</string> <string name="pref_switch_card_view_on">"Els articles es mostraran com a targetes"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Zeige Anzahl ungelesener Artikel"</string> <string name="switch_unread_count_title">"Zeige Anzahl ungelesener Artikel"</string>
<string name="display_all_counts_title">"Zeige Anzahl der Favoriten und gelesenen Artikel"</string> <string name="display_all_counts_title">"Zeige Anzahl der Favoriten und gelesenen Artikel"</string>
<string name="text_wrong_url">"Sie scheinen eine ungültige URL verwenden. Stellen Sie sicher, dass die URL richtig ist. Sollte das Problem weiterhin bestehen kontaktieren Sie mich (über den Playstore-Kontakt-Link). Bitte beachten Sie, dass Sie Selfoss benötigen um RSS-Feeds zu lesen."</string> <string name="text_wrong_url">"Sie scheinen eine ungültige URL verwenden. Stellen Sie sicher, dass die URL richtig ist. Sollte das Problem weiterhin bestehen kontaktieren Sie mich (über den Playstore-Kontakt-Link). Bitte beachten Sie, dass Sie Selfoss benötigen um RSS-Feeds zu lesen."</string>
<string name="pref_article_viewer_title">"Öffne Links innerhalb der App"</string> <string name="pref_general_internal_browser_title">"Öffne Links innerhalb der App"</string>
<string name="pref_article_viewer_on">"Artikel werden innerhalb der App geöffnet"</string> <string name="pref_general_internal_browser_on">"Artikel werden innerhalb der App geöffnet"</string>
<string name="pref_article_viewer_off">"Artikel werden mit deinem Standard-Browser geöffnet"</string> <string name="pref_general_internal_browser_off">"Artikel werden mit deinem Standard-Browser geöffnet"</string>
<string name="prefer_article_viewer_title">"Verwenden Sie den Artikel-viewer"</string>
<string name="prefer_article_viewer_on">"Artikel-Viewer wird anstelle des internen Browser verwendet"</string>
<string name="prefer_article_viewer_off">"Der internen Browser wird anstelle des Artikel-Viewer verwendet"</string>
<string name="pref_general_category_links">"Umgang mit Links"</string> <string name="pref_general_category_links">"Umgang mit Links"</string>
<string name="pref_general_category_displaying">"Ansicht"</string> <string name="pref_general_category_displaying">"Ansicht"</string>
<string name="pref_switch_card_view_on">"Artikel werden als Kacheln angezeigt"</string> <string name="pref_switch_card_view_on">"Artikel werden als Kacheln angezeigt"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Mostrar recuento no leído"</string> <string name="switch_unread_count_title">"Mostrar recuento no leído"</string>
<string name="display_all_counts_title">"Mostrar recuento de favoritos y leídos"</string> <string name="display_all_counts_title">"Mostrar recuento de favoritos y leídos"</string>
<string name="text_wrong_url">"Parece estar tratando de utilizar una dirección URL inválida. Asegúrese de que sea correcta y si el problema persiste, póngase en contacto conmigo (mediante el enlace de contacto de la tienda). Tenga en cuenta que la aplicación necesita utilizar Selfoss. No se puede acceder al contenido RSS sin él."</string> <string name="text_wrong_url">"Parece estar tratando de utilizar una dirección URL inválida. Asegúrese de que sea correcta y si el problema persiste, póngase en contacto conmigo (mediante el enlace de contacto de la tienda). Tenga en cuenta que la aplicación necesita utilizar Selfoss. No se puede acceder al contenido RSS sin él."</string>
<string name="pref_article_viewer_title">"Abrir enlaces dentro de la aplicación"</string> <string name="pref_general_internal_browser_title">"Abrir enlaces dentro de la aplicación"</string>
<string name="pref_article_viewer_on">"Los artículos se abrirán dentro de la aplicación"</string> <string name="pref_general_internal_browser_on">"Los artículos se abrirán dentro de la aplicación"</string>
<string name="pref_article_viewer_off">"Los artículos se abrirán con tu navegador predeterminado"</string> <string name="pref_general_internal_browser_off">"Los artículos se abrirán con tu navegador predeterminado"</string>
<string name="prefer_article_viewer_title">"Utilizar el visor de artículo"</string>
<string name="prefer_article_viewer_on">"Se usará el visor de artículos en lugar del navegador interno"</string>
<string name="prefer_article_viewer_off">"Se utilizará el navegador interno en lugar del visor de artículo"</string>
<string name="pref_general_category_links">"Control de enlaces"</string> <string name="pref_general_category_links">"Control de enlaces"</string>
<string name="pref_general_category_displaying">"Mostrando"</string> <string name="pref_general_category_displaying">"Mostrando"</string>
<string name="pref_switch_card_view_on">"Los artículos se mostrarán como tarjetas"</string> <string name="pref_switch_card_view_on">"Los artículos se mostrarán como tarjetas"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Display unread count"</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="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="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_article_viewer_title">"Open links inside the app"</string> <string name="pref_general_internal_browser_title">"Open links inside the app"</string>
<string name="pref_article_viewer_on">"Articles will open inside the app"</string> <string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
<string name="pref_article_viewer_off">"Articles will open with your default browser"</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_links">"Link handling"</string>
<string name="pref_general_category_displaying">"Displaying"</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_on">"The articles will be displayed as cards"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -8,7 +8,7 @@
<string name="error_field_required">"Champ requis"</string> <string name="error_field_required">"Champ requis"</string>
<string name="prompt_url">"Url Selfoss"</string> <string name="prompt_url">"Url Selfoss"</string>
<string name="withLoginSwitch">"Avec login ?"</string> <string name="withLoginSwitch">"Avec login ?"</string>
<string name="login_url_problem">"Petit souci. Il manque peut-être un / à la fin ?"</string> <string name="login_url_problem">"Petit souci. Il manque peut être un / à la fin ?"</string>
<string name="prompt_login">"Utilisateur"</string> <string name="prompt_login">"Utilisateur"</string>
<string name="label_share">"Partager"</string> <string name="label_share">"Partager"</string>
<string name="readAll">"Tout lire"</string> <string name="readAll">"Tout lire"</string>
@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Afficher le nombre de non lus"</string> <string name="switch_unread_count_title">"Afficher le nombre de non lus"</string>
<string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string> <string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string>
<string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string> <string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string>
<string name="pref_article_viewer_title">"Ouvrir les liens dans l'application"</string> <string name="pref_general_internal_browser_title">"Ouvrir les liens dans l'application"</string>
<string name="pref_article_viewer_on">"Les articles s'ouvriront dans l'application"</string> <string name="pref_general_internal_browser_on">"Les articles s'ouvriront dans l'application"</string>
<string name="pref_article_viewer_off">"Les articles s'ouvriront dans votre naviguateur par défaut"</string> <string name="pref_general_internal_browser_off">"Les articles s'ouvriront dans votre naviguateur par défaut"</string>
<string name="prefer_article_viewer_title">"Utiliser le visionneur d'articles"</string>
<string name="prefer_article_viewer_on">"Utiliser le naviguateur interne"</string>
<string name="prefer_article_viewer_off">"Utiliser le naviguateur interne au lieu du visionneur d'articles"</string>
<string name="pref_general_category_links">"Gestion des liens"</string> <string name="pref_general_category_links">"Gestion des liens"</string>
<string name="pref_general_category_displaying">"Affichage"</string> <string name="pref_general_category_displaying">"Affichage"</string>
<string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string> <string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">La barre sera affichée</string> <string name="reader_static_bar_on">La barre sera affichée</string>
<string name="reader_static_bar_off">La barre sera affichée grâce au bouton</string> <string name="reader_static_bar_off">La barre sera affichée grâce au bouton</string>
<string name="remove_source">Supprimer la source</string> <string name="remove_source">Supprimer la source</string>
<string name="pref_theme_title">Thème Clair/Sombre</string>
<string name="mode_dark">Thème sombre</string>
<string name="mode_system">Utiliser les paramètres système</string>
<string name="mode_light">Thème clair</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Mostrar reconto de artigos non lidos"</string> <string name="switch_unread_count_title">"Mostrar reconto de artigos non lidos"</string>
<string name="display_all_counts_title">"Mostrar reconto de artigos lidos e favoritos"</string> <string name="display_all_counts_title">"Mostrar reconto de artigos lidos e favoritos"</string>
<string name="text_wrong_url">"Semella que intentas usar unha URL non válida. Asegúrate de que é correcta, e se o problema persiste, ponte en contacto conmigo (a través da ligazón de contacto na tenda). Por favor ten en conta que a aplicación precisa que uses Selfoss. Non podes acceder a canles RSS se non o tes."</string> <string name="text_wrong_url">"Semella que intentas usar unha URL non válida. Asegúrate de que é correcta, e se o problema persiste, ponte en contacto conmigo (a través da ligazón de contacto na tenda). Por favor ten en conta que a aplicación precisa que uses Selfoss. Non podes acceder a canles RSS se non o tes."</string>
<string name="pref_article_viewer_title">"Abrir ligazóns dentro da aplicación"</string> <string name="pref_general_internal_browser_title">"Abrir ligazóns dentro da aplicación"</string>
<string name="pref_article_viewer_on">"Os artigos abriranse dentro da aplicación"</string> <string name="pref_general_internal_browser_on">"Os artigos abriranse dentro da aplicación"</string>
<string name="pref_article_viewer_off">"Os artigos abriranse co teu navegador prederminado"</string> <string name="pref_general_internal_browser_off">"Os artigos abriranse co teu navegador prederminado"</string>
<string name="prefer_article_viewer_title">"Usar o visor de artigos"</string>
<string name="prefer_article_viewer_on">"Usarase o visor de artigos en lugar do navegador interno"</string>
<string name="prefer_article_viewer_off">"Usarase o navegador interno en lugar do visor de artigos"</string>
<string name="pref_general_category_links">"Xestión de ligazóns"</string> <string name="pref_general_category_links">"Xestión de ligazóns"</string>
<string name="pref_general_category_displaying">"Visualización"</string> <string name="pref_general_category_displaying">"Visualización"</string>
<string name="pref_switch_card_view_on">"Os artigos amosaranse coma tarxetas"</string> <string name="pref_switch_card_view_on">"Os artigos amosaranse coma tarxetas"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">A barra inferior mostrarase sempre</string> <string name="reader_static_bar_on">A barra inferior mostrarase sempre</string>
<string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string> <string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string>
<string name="remove_source">Eliminar fonte</string> <string name="remove_source">Eliminar fonte</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Tampilkan jumlah item yang belum dibaca"</string> <string name="switch_unread_count_title">"Tampilkan jumlah item yang belum dibaca"</string>
<string name="display_all_counts_title">"Tampilkan jumlah item untuk favorit dan sudah dibaca"</string> <string name="display_all_counts_title">"Tampilkan jumlah item untuk favorit dan sudah dibaca"</string>
<string name="text_wrong_url">"Sepertinya Anda mencoba menggunakan URL yang tidak valid. Pastikan itu benar, jika masalah terus berlanjut, hubungi saya (melalui link kontak toko). Harap dicatat bahwa aplikasi ini mengharuskan Anda menggunakan Selfoss. Tanpa itu, Anda tidak bisa mengakses umpan RSS."</string> <string name="text_wrong_url">"Sepertinya Anda mencoba menggunakan URL yang tidak valid. Pastikan itu benar, jika masalah terus berlanjut, hubungi saya (melalui link kontak toko). Harap dicatat bahwa aplikasi ini mengharuskan Anda menggunakan Selfoss. Tanpa itu, Anda tidak bisa mengakses umpan RSS."</string>
<string name="pref_article_viewer_title">"Buka tautan dalam aplikasi"</string> <string name="pref_general_internal_browser_title">"Buka tautan dalam aplikasi"</string>
<string name="pref_article_viewer_on">"Artikel akan dibuka di dalam aplikasi"</string> <string name="pref_general_internal_browser_on">"Artikel akan dibuka di dalam aplikasi"</string>
<string name="pref_article_viewer_off">"Artikel akan dibuka dalam peramban bawaan Anda"</string> <string name="pref_general_internal_browser_off">"Artikel akan dibuka dalam peramban bawaan Anda"</string>
<string name="prefer_article_viewer_title">"Gunakan pratinjau artikel"</string>
<string name="prefer_article_viewer_on">"Lihat artikel di penampil daripada peramban internal"</string>
<string name="prefer_article_viewer_off">"Gunakan peramban internal dan bukan penampil artikel"</string>
<string name="pref_general_category_links">"Pengolahan tautan"</string> <string name="pref_general_category_links">"Pengolahan tautan"</string>
<string name="pref_general_category_displaying">"Tampilan"</string> <string name="pref_general_category_displaying">"Tampilan"</string>
<string name="pref_switch_card_view_on">"Artikel ini akan ditampilkan dalam bentuk kartu"</string> <string name="pref_switch_card_view_on">"Artikel ini akan ditampilkan dalam bentuk kartu"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Display unread count"</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="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="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_article_viewer_title">"Open links inside the app"</string> <string name="pref_general_internal_browser_title">"Open links inside the app"</string>
<string name="pref_article_viewer_on">"Articles will open inside the app"</string> <string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
<string name="pref_article_viewer_off">"Articles will open with your default browser"</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_links">"Link handling"</string>
<string name="pref_general_category_displaying">"Displaying"</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_on">"The articles will be displayed as cards"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Display unread count"</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="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="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_article_viewer_title">"Open links inside the app"</string> <string name="pref_general_internal_browser_title">"Open links inside the app"</string>
<string name="pref_article_viewer_on">"Articles will open inside the app"</string> <string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
<string name="pref_article_viewer_off">"Articles will open with your default browser"</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_links">"Link handling"</string>
<string name="pref_general_category_displaying">"Displaying"</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_on">"The articles will be displayed as cards"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -1,13 +0,0 @@
<resources>
<style name="NoBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorAccentDark">@color/colorAccentDark</item>
<item name="preferenceTheme">@style/PreferenceStyle</item>
<item name="android:statusBarColor">@color/dark</item>
<item name="bottomBarBackground">@color/dark</item>
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
<item name="webviewBackground">@color/dark</item>
</style>
</resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Geef aantal ongelezen weer"</string> <string name="switch_unread_count_title">"Geef aantal ongelezen weer"</string>
<string name="display_all_counts_title">"Geef aantal weer bij favorieten en gelezen"</string> <string name="display_all_counts_title">"Geef aantal weer bij favorieten en gelezen"</string>
<string name="text_wrong_url">"De gebruikte link lijkt onjuist. Controleer deze. Mocht het probleem blijven, neem dan contact met me op (via de contact link in de store). Om deze app te kunnen gebruiken heb je Selfoss nodig."</string> <string name="text_wrong_url">"De gebruikte link lijkt onjuist. Controleer deze. Mocht het probleem blijven, neem dan contact met me op (via de contact link in de store). Om deze app te kunnen gebruiken heb je Selfoss nodig."</string>
<string name="pref_article_viewer_title">"Links opnemen in interne browser"</string> <string name="pref_general_internal_browser_title">"Links opnemen in interne browser"</string>
<string name="pref_article_viewer_on">"Artikelen worden in de interne browser geopend"</string> <string name="pref_general_internal_browser_on">"Artikelen worden in de interne browser geopend"</string>
<string name="pref_article_viewer_off">"Artikelen worden geopend in de standaard browser"</string> <string name="pref_general_internal_browser_off">"Artikelen worden geopend in de standaard browser"</string>
<string name="prefer_article_viewer_title">"Gebruik artikel viewer"</string>
<string name="prefer_article_viewer_on">"Artikelen in viewer weergeven in plaats van de interne browser"</string>
<string name="prefer_article_viewer_off">"Artikelen in interne browser weergeven in plaats van viewer"</string>
<string name="pref_general_category_links">"Links"</string> <string name="pref_general_category_links">"Links"</string>
<string name="pref_general_category_displaying">"Weergave"</string> <string name="pref_general_category_displaying">"Weergave"</string>
<string name="pref_switch_card_view_on">"De artikelen worden als kaarten weergegeven"</string> <string name="pref_switch_card_view_on">"De artikelen worden als kaarten weergegeven"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Exibir contagem de artigos não lidos"</string> <string name="switch_unread_count_title">"Exibir contagem de artigos não lidos"</string>
<string name="display_all_counts_title">"Exibir contagem de lidos e favoritos"</string> <string name="display_all_counts_title">"Exibir contagem de lidos e favoritos"</string>
<string name="text_wrong_url">"Parece que você está tentando utilizar uma URL inválida. Certifique-se de que está correto, e se o problema persistir, entre em contato comigo (através do link de contato da loja). Por favor, note que o aplicativo precisa que você esteja usando o Selfoss. Você não pode acessar feeds RSS sem ele."</string> <string name="text_wrong_url">"Parece que você está tentando utilizar uma URL inválida. Certifique-se de que está correto, e se o problema persistir, entre em contato comigo (através do link de contato da loja). Por favor, note que o aplicativo precisa que você esteja usando o Selfoss. Você não pode acessar feeds RSS sem ele."</string>
<string name="pref_article_viewer_title">"Abrir links dentro do aplicativo"</string> <string name="pref_general_internal_browser_title">"Abrir links dentro do aplicativo"</string>
<string name="pref_article_viewer_on">"Os artigos serão abertos dentro do aplicativo"</string> <string name="pref_general_internal_browser_on">"Os artigos serão abertos dentro do aplicativo"</string>
<string name="pref_article_viewer_off">"Os artigos serão abertos com seu navegador padrão"</string> <string name="pref_general_internal_browser_off">"Os artigos serão abertos com seu navegador padrão"</string>
<string name="prefer_article_viewer_title">"Use o visualizador de artigos"</string>
<string name="prefer_article_viewer_on">"Usará o visualizador de artigos em vez do navegador"</string>
<string name="prefer_article_viewer_off">"Utilizará o navegador em vez do visualizador de artigos"</string>
<string name="pref_general_category_links">"Manipulação de links"</string> <string name="pref_general_category_links">"Manipulação de links"</string>
<string name="pref_general_category_displaying">"Mostrando"</string> <string name="pref_general_category_displaying">"Mostrando"</string>
<string name="pref_switch_card_view_on">"Os artigos serão exibidos no formato de cards"</string> <string name="pref_switch_card_view_on">"Os artigos serão exibidos no formato de cards"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Exibir a contagem não lida"</string> <string name="switch_unread_count_title">"Exibir a contagem não lida"</string>
<string name="display_all_counts_title">"Exibir a contagem para o favorito e leitura"</string> <string name="display_all_counts_title">"Exibir a contagem para o favorito e leitura"</string>
<string name="text_wrong_url">"Você parece estar tentando usar um URL inválido. Certifique-se de que está correto, e se o problema persistir, entre em contato comigo (através do link de contato da loja). Por favor, note que o aplicativo precisa que você esteja usando o Selfoss. Você não pode acessar feeds RSS sem ele."</string> <string name="text_wrong_url">"Você parece estar tentando usar um URL inválido. Certifique-se de que está correto, e se o problema persistir, entre em contato comigo (através do link de contato da loja). Por favor, note que o aplicativo precisa que você esteja usando o Selfoss. Você não pode acessar feeds RSS sem ele."</string>
<string name="pref_article_viewer_title">"Abrir links dentro do app"</string> <string name="pref_general_internal_browser_title">"Abrir links dentro do app"</string>
<string name="pref_article_viewer_on">"Artigos serão aberto dentro do aplicativo"</string> <string name="pref_general_internal_browser_on">"Artigos serão aberto dentro do aplicativo"</string>
<string name="pref_article_viewer_off">"Artigos serão aberto com o seu navegador padrão"</string> <string name="pref_general_internal_browser_off">"Artigos serão aberto com o seu navegador padrão"</string>
<string name="prefer_article_viewer_title">"Use o Visualizador de artigo"</string>
<string name="prefer_article_viewer_on">"Vai usar o Visualizador de artigo em vez do navegador interno"</string>
<string name="prefer_article_viewer_off">"Vai usar o navegador interno em vez do Visualizador de artigo"</string>
<string name="pref_general_category_links">"Manipulação de ligações"</string> <string name="pref_general_category_links">"Manipulação de ligações"</string>
<string name="pref_general_category_displaying">"Mostrando"</string> <string name="pref_general_category_displaying">"Mostrando"</string>
<string name="pref_switch_card_view_on">"Os artigos serão exibidos como cartões"</string> <string name="pref_switch_card_view_on">"Os artigos serão exibidos como cartões"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Display unread count"</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="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="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_article_viewer_title">"Open links inside the app"</string> <string name="pref_general_internal_browser_title">"Open links inside the app"</string>
<string name="pref_article_viewer_on">"Articles will open inside the app"</string> <string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
<string name="pref_article_viewer_off">"Articles will open with your default browser"</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_links">"Link handling"</string>
<string name="pref_general_category_displaying">"Displaying"</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_on">"The articles will be displayed as cards"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"Okunmamış sayıyı görüntüle"</string> <string name="switch_unread_count_title">"Okunmamış sayıyı görüntüle"</string>
<string name="display_all_counts_title">"Favori ve okunan sayıları göster"</string> <string name="display_all_counts_title">"Favori ve okunan sayıları göster"</string>
<string name="text_wrong_url">"Geçersiz bir URL kullanmaya çalışıyormuş gibi görünüyorsunuz. Doğru olduğundan emin olun ve sorun devam ederse, bana ulaşın (mağaza iletişim bağlantısıyla). Uygulamanın, Selfoss'u kullanmanız gerektiğini lütfen unutmayın. RSS özet akışlarına olmadan erişemezsiniz."</string> <string name="text_wrong_url">"Geçersiz bir URL kullanmaya çalışıyormuş gibi görünüyorsunuz. Doğru olduğundan emin olun ve sorun devam ederse, bana ulaşın (mağaza iletişim bağlantısıyla). Uygulamanın, Selfoss'u kullanmanız gerektiğini lütfen unutmayın. RSS özet akışlarına olmadan erişemezsiniz."</string>
<string name="pref_article_viewer_title">"Uygulamadaki bağlantılarıın"</string> <string name="pref_general_internal_browser_title">"Uygulamadaki bağlantılarıın"</string>
<string name="pref_article_viewer_on">"Makale, uygulama içinde açılacaktır"</string> <string name="pref_general_internal_browser_on">"Makale, uygulama içinde açılacaktır"</string>
<string name="pref_article_viewer_off">"Makaleler varsayılan tarayıcınızla açılır"</string> <string name="pref_general_internal_browser_off">"Makaleler varsayılan tarayıcınızla açılır"</string>
<string name="prefer_article_viewer_title">"Makale görüntüleyiciyi kullanın"</string>
<string name="prefer_article_viewer_on">"Dahili tarayıcı yerine makale görüntüleyicisini kullanacak"</string>
<string name="prefer_article_viewer_off">"Makale görüntüleyicisi yerine dahili tarayıcıyı kullanacak"</string>
<string name="pref_general_category_links">"Bağlantı açma şekli"</string> <string name="pref_general_category_links">"Bağlantı açma şekli"</string>
<string name="pref_general_category_displaying">"Gösteriliyor"</string> <string name="pref_general_category_displaying">"Gösteriliyor"</string>
<string name="pref_switch_card_view_on">"Makaleler kart olarak gösterilecek"</string> <string name="pref_switch_card_view_on">"Makaleler kart olarak gösterilecek"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"显示未读数"</string> <string name="switch_unread_count_title">"显示未读数"</string>
<string name="display_all_counts_title">"显示收藏和已读的计数"</string> <string name="display_all_counts_title">"显示收藏和已读的计数"</string>
<string name="text_wrong_url">"您似乎试图使用无效的 URL。确保它是正确的如果问题仍然存在请与我联系 (通过商店的联系链接)。请注意,该应用程序需要您使用 Selfoss。没有它您无法访问 RSS 源。"</string> <string name="text_wrong_url">"您似乎试图使用无效的 URL。确保它是正确的如果问题仍然存在请与我联系 (通过商店的联系链接)。请注意,该应用程序需要您使用 Selfoss。没有它您无法访问 RSS 源。"</string>
<string name="pref_article_viewer_title">"在应用内打开链接"</string> <string name="pref_general_internal_browser_title">"打开应用程序中的链接"</string>
<string name="pref_article_viewer_on">"文章将在应用程序内打开"</string> <string name="pref_general_internal_browser_on">"文章将在应用程序内打开"</string>
<string name="pref_article_viewer_off">"文章将使用默认浏览器打开"</string> <string name="pref_general_internal_browser_off">"文章将使用默认浏览器打开"</string>
<string name="prefer_article_viewer_title">"使用文章查看器"</string>
<string name="prefer_article_viewer_on">"将使用文章查看器而不是内部浏览器"</string>
<string name="prefer_article_viewer_off">"将使用内部浏览器而不是文章查看器"</string>
<string name="pref_general_category_links">"链接处理"</string> <string name="pref_general_category_links">"链接处理"</string>
<string name="pref_general_category_displaying">"显示"</string> <string name="pref_general_category_displaying">"显示"</string>
<string name="pref_switch_card_view_on">"这些文章将以卡片形式显示"</string> <string name="pref_switch_card_view_on">"这些文章将以卡片形式显示"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">底部栏将始终显示</string> <string name="reader_static_bar_on">底部栏将始终显示</string>
<string name="reader_static_bar_off">底部栏可以通过浮动按钮显示</string> <string name="reader_static_bar_off">底部栏可以通过浮动按钮显示</string>
<string name="remove_source">删除源</string> <string name="remove_source">删除源</string>
<string name="pref_theme_title">浅色/深色模式</string>
<string name="mode_dark">深色模式</string>
<string name="mode_system">遵循系统设置</string>
<string name="mode_light">浅色模式</string>
</resources> </resources>

View File

@ -47,9 +47,12 @@
<string name="switch_unread_count_title">"显示未读数"</string> <string name="switch_unread_count_title">"显示未读数"</string>
<string name="display_all_counts_title">"显示收藏和已读的计数"</string> <string name="display_all_counts_title">"显示收藏和已读的计数"</string>
<string name="text_wrong_url">"您似乎试图使用无效的 URL。确保它是正确的如果问题仍然存在请与我联系 (通过商店的联系链接)。请注意,该应用程序需要您使用 Selfoss。没有它您无法访问 RSS 源。"</string> <string name="text_wrong_url">"您似乎试图使用无效的 URL。确保它是正确的如果问题仍然存在请与我联系 (通过商店的联系链接)。请注意,该应用程序需要您使用 Selfoss。没有它您无法访问 RSS 源。"</string>
<string name="pref_article_viewer_title">"打开应用程序中的链接"</string> <string name="pref_general_internal_browser_title">"打开应用程序中的链接"</string>
<string name="pref_article_viewer_on">"文章将在应用程序内打开"</string> <string name="pref_general_internal_browser_on">"文章将在应用程序内打开"</string>
<string name="pref_article_viewer_off">"文章将使用默认浏览器打开"</string> <string name="pref_general_internal_browser_off">"文章将使用默认浏览器打开"</string>
<string name="prefer_article_viewer_title">"使用文章查看器"</string>
<string name="prefer_article_viewer_on">"将使用文章查看器而不是内部浏览器"</string>
<string name="prefer_article_viewer_off">"将使用内部浏览器而不是文章查看器"</string>
<string name="pref_general_category_links">"链接处理"</string> <string name="pref_general_category_links">"链接处理"</string>
<string name="pref_general_category_displaying">"显示"</string> <string name="pref_general_category_displaying">"显示"</string>
<string name="pref_switch_card_view_on">"这些文章将以卡片形式显示"</string> <string name="pref_switch_card_view_on">"这些文章将以卡片形式显示"</string>
@ -128,8 +131,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -2,8 +2,5 @@
<resources> <resources>
<declare-styleable name="Theme"> <declare-styleable name="Theme">
<attr name="colorAccentDark" format="reference|color" /> <attr name="colorAccentDark" format="reference|color" />
<attr name="bottomBarBackground" format="reference|color" />
<attr name="toolbarPopupTheme" format="reference|color" />
<attr name="webviewBackground" format="reference|color" />
</declare-styleable> </declare-styleable>
</resources> </resources>

View File

@ -11,5 +11,6 @@
<color name="refresh_progress_1">@color/colorAccentDark</color> <color name="refresh_progress_1">@color/colorAccentDark</color>
<color name="refresh_progress_2">@color/colorAccent</color> <color name="refresh_progress_2">@color/colorAccent</color>
<color name="refresh_progress_3">@color/pink</color> <color name="refresh_progress_3">@color/pink</color>
<color name="dark">#FF282828</color>
<color name="darkBackground">#303030</color>
</resources> </resources>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="ModeTitles">
<item>@string/mode_light</item>
<item>@string/mode_dark</item>
<item>@string/mode_system</item>
</string-array>
<string-array name="ModeValues">
<item>1</item> <!--MODE_NIGHT_NO-->
<item>2</item> <!--MODE_NIGHT_YES-->
<item>0</item> <!--MODE_NIGHT_AUTO_TIME-->
</string-array>
<string-array name="Voice">
<item>Male</item>
<item>Female</item>
</string-array>
<string-array name="VoiceAlias">
<item>"usenglishmale"</item>
<item>"usenglishfemale"</item>
<item>"ukenglishmale"</item>
<item>"ukenglishfemale"</item>
<item>"eurfrenchmale"</item>
<item>"eurfrenchfemale"</item>
<item>"eurspanishmale"</item>
<item>"eurspanishfemale"</item>
<item>"euritalianmale"</item>
<item>"euritalianfemale"</item>
</string-array>
</resources>

View File

@ -46,9 +46,12 @@
<string name="switch_unread_count_title">"Display unread count"</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="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="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_article_viewer_title">"Open links inside the app"</string> <string name="pref_general_internal_browser_title">"Open links inside the app"</string>
<string name="pref_article_viewer_on">"Articles will open inside the app"</string> <string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
<string name="pref_article_viewer_off">"Articles will open with your default browser"</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_links">"Link handling"</string>
<string name="pref_general_category_displaying">"Displaying"</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_on">"The articles will be displayed as cards"</string>
@ -130,8 +133,4 @@
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
</resources> </resources>

View File

@ -4,23 +4,41 @@
<item name="android:windowBackground">@drawable/background_splash</item> <item name="android:windowBackground">@drawable/background_splash</item>
</style> </style>
<style name="NoBar" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <style name="NoBar" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="colorAccentDark">@color/colorAccentDark</item> <item name="colorAccentDark">@color/colorAccentDark</item>
<item name="preferenceTheme">@style/PreferenceStyle</item> <item name="cardBackgroundColor">@color/white</item>
<item name="android:statusBarColor">?attr/colorPrimary</item> <item name="android:colorBackground">@color/grey_50</item>
<item name="bottomBarBackground">@color/white</item> <item name="colorSurface">@color/grey_50</item>
<item name="android:textColorPrimary">@color/grey_900</item>
<item name="android:textColorSecondary">@color/grey_400</item>
<item name="materialDrawerStyle">@style/App.materialDrawerStyle</item>
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item> <item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
<item name="webviewBackground">@color/white</item> <item name="preferenceTheme">@style/PreferenceStyle</item>
</style> </style>
<!-- Preference Theme --> <style name="NoBarDark" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<style name="PreferenceStyle" parent="@style/PreferenceThemeOverlay"> <item name="colorPrimary">@color/colorPrimary</item>
<item name="android:tint">?attr/colorOnSurface</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorAccentDark">@color/colorAccentDark</item>
<item name="cardBackgroundColor">@color/grey_800</item>
<item name="android:colorBackground">@color/darkBackground</item>
<item name="colorSurface">@color/darkBackground</item>
<item name="alertDialogTheme">@style/AlertDialogDark</item>
<item name="bnbBackgroundColor">@color/grey_900</item>
<item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorSecondary">@color/grey_600</item>
<item name="materialDrawerStyle">@style/App.materialDrawerStyle</item>
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
<item name="preferenceTheme">@style/PreferenceStyle</item>
</style> </style>
<!-- ToolBar -->
<style name="ToolBarStyle" parent="Theme.AppCompat"> <style name="ToolBarStyle" parent="Theme.AppCompat">
<item name="android:textColorPrimary">@color/white</item> <item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorSecondary">@color/white</item> <item name="android:textColorSecondary">@color/white</item>
@ -28,4 +46,28 @@
<!--<item name="actionOverflowButtonStyle">@style/ActionButtonOverflowStyle</item> <!--<item name="actionOverflowButtonStyle">@style/ActionButtonOverflowStyle</item>
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>--> <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>-->
</style> </style>
<!-- Material Drawer Theme -->
<style name="App.materialDrawerStyle" parent="@style/Widget.MaterialDrawerStyle">
<item name="materialDrawerPrimaryIcon">?android:textColorPrimary</item>
<item name="materialDrawerSecondaryIcon">?android:textColorPrimary</item>
<item name="materialDrawerSecondaryText">?android:textColorPrimary</item>
</style>
<!-- Preference Theme -->
<style name="PreferenceStyle" parent="@style/PreferenceThemeOverlay">
<item name="android:tint">?android:textColorPrimary</item>
</style>
<!-- Spinner Theme -->
<style name="App.Spinner" parent="Widget.AppCompat.Light.DropDownItem.Spinner">
<item name="android:textColor">?android:textColorPrimary</item>
</style>
<!-- Alert dialog Theme -->
<style name="AlertDialogDark" parent="Theme.MaterialComponents.Dialog">
<item name="android:background">@color/darkBackground</item>
</style>
</resources> </resources>

View File

@ -6,7 +6,7 @@
</PreferenceCategory> </PreferenceCategory>
<EditTextPreference <EditTextPreference
android:defaultValue="20" android:defaultValue="200"
android:inputType="number" android:inputType="number"
android:key="prefer_api_items_number" android:key="prefer_api_items_number"
android:selectAllOnFocus="true" android:selectAllOnFocus="true"
@ -34,10 +34,18 @@
</PreferenceCategory> </PreferenceCategory>
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:key="prefer_internal_browser"
android:summaryOff="@string/pref_general_internal_browser_off"
android:summaryOn="@string/pref_general_internal_browser_on"
android:title="@string/pref_general_internal_browser_title"
app:iconSpaceReserved="false"/>
<SwitchPreference
android:defaultValue="true"
android:dependency="prefer_internal_browser"
android:key="prefer_article_viewer" android:key="prefer_article_viewer"
android:summaryOff="@string/pref_article_viewer_off" android:summaryOff="@string/prefer_article_viewer_off"
android:summaryOn="@string/pref_article_viewer_on" android:summaryOn="@string/prefer_article_viewer_on"
android:title="@string/pref_article_viewer_title" android:title="@string/prefer_article_viewer_title"
app:iconSpaceReserved="false"/> app:iconSpaceReserved="false"/>
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="false"

View File

@ -1,13 +1,35 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- TODO translate this file -->
<ListPreference <SwitchPreference
android:defaultValue="0" android:defaultValue="false"
android:entries="@array/ModeTitles" android:key="dark_theme"
android:entryValues="@array/ModeValues"
android:key="currentMode"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
android:title="@string/pref_theme_title" android:title="Dark theme" />
app:useSimpleSummaryProvider="false" />
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
android:defaultValue="@color/colorPrimary"
android:key="color_primary"
app:iconSpaceReserved="false"
android:title="Primary color"/>
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
android:defaultValue="@color/colorPrimaryDark"
android:key="color_primary_dark"
app:iconSpaceReserved="false"
android:title="Primary dark color"/>
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
android:defaultValue="@color/colorAccent"
android:key="color_accent"
app:iconSpaceReserved="false"
android:title="Accent color"/>
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
android:defaultValue="@color/colorAccentDark"
android:key="color_accent_dark"
app:iconSpaceReserved="false"
android:title="Accent dark color"/>
</PreferenceScreen> </PreferenceScreen>

View File

@ -28,12 +28,12 @@ kotlin {
sourceSets { sourceSets {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation("io.ktor:ktor-client-core:2.1.1") implementation("io.ktor:ktor-client-core:2.0.1")
implementation("io.ktor:ktor-client-content-negotiation:2.1.1") implementation("io.ktor:ktor-client-content-negotiation:2.0.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.1") implementation("io.ktor:ktor-serialization-kotlinx-json:2.0.1")
implementation("io.ktor:ktor-client-logging:2.1.1") implementation("io.ktor:ktor-client-logging:2.0.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("io.ktor:ktor-client-auth:2.1.1") implementation("io.ktor:ktor-client-auth:2.0.1")
implementation("org.jsoup:jsoup:1.14.3") implementation("org.jsoup:jsoup:1.14.3")
//Dependency Injection //Dependency Injection
@ -56,13 +56,11 @@ kotlin {
dependencies { dependencies {
implementation(kotlin("test-common")) implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common")) implementation(kotlin("test-annotations-common"))
implementation("io.mockk:mockk:1.12.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
} }
} }
val androidMain by getting { val androidMain by getting {
dependencies { dependencies {
implementation("io.ktor:ktor-client-okhttp:2.1.1") implementation("io.ktor:ktor-client-android:2.0.1")
// Sql // Sql
implementation(SqlDelight.android) implementation(SqlDelight.android)
@ -96,7 +94,7 @@ kotlin {
iosX64Test.dependsOn(this) iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this) iosArm64Test.dependsOn(this)
dependencies { dependencies {
implementation("io.ktor:ktor-client-ios:2.1.1") implementation("io.ktor:ktor-client-ios:2.0.1")
} }
//iosSimulatorArm64Test.dependsOn(this) //iosSimulatorArm64Test.dependsOn(this)
} }

View File

@ -2,14 +2,7 @@ package bou.amine.apps.readerforselfossv2.model
import bou.amine.apps.readerforselfossv2.utils.DateUtils import bou.amine.apps.readerforselfossv2.utils.DateUtils
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
class SelfossModel { class SelfossModel {
@ -57,7 +50,6 @@ class SelfossModel {
data class Source( data class Source(
val id: Int, val id: Int,
val title: String, val title: String,
@Serializable(with = TagsListSerializer::class)
val tags: List<String>, val tags: List<String>,
val spout: String, val spout: String,
val error: String, val error: String,
@ -70,15 +62,12 @@ class SelfossModel {
val datetime: String, val datetime: String,
val title: String, val title: String,
val content: String, val content: String,
@Serializable(with = BooleanSerializer::class)
var unread: Boolean, var unread: Boolean,
@Serializable(with = BooleanSerializer::class)
var starred: Boolean, var starred: Boolean,
val thumbnail: String?, val thumbnail: String?,
val icon: String?, val icon: String?,
val link: String, val link: String,
val sourcetitle: String, val sourcetitle: String,
@Serializable(with = TagsListSerializer::class)
val tags: List<String> val tags: List<String>
) { ) {
// TODO: maybe find a better way to handle these kind of urls // TODO: maybe find a better way to handle these kind of urls
@ -117,38 +106,6 @@ class SelfossModel {
} }
} }
// TODO: this seems to be super slow.
object TagsListSerializer : KSerializer<List<String>> {
override fun deserialize(decoder: Decoder): List<String> {
return when(val json = ((decoder as JsonDecoder).decodeJsonElement())) {
is JsonArray -> json.toList().map { it.toString() }
else -> json.toString().split(",")
}
}
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: List<String>) {
TODO("Not yet implemented")
}
}
object BooleanSerializer : KSerializer<Boolean> {
override fun deserialize(decoder: Decoder): Boolean {
val json = ((decoder as JsonDecoder).decodeJsonElement()).jsonPrimitive
return json.booleanOrNull ?: json.int == 1
}
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("b", PrimitiveKind.BOOLEAN)
override fun serialize(encoder: Encoder, value: Boolean) {
TODO("Not yet implemented")
}
}
class StatusAndData<T>(val success: Boolean, val data: T? = null) { class StatusAndData<T>(val success: Boolean, val data: T? = null) {
companion object { companion object {
fun <T> succes(d: T): StatusAndData<T> { fun <T> succes(d: T): StatusAndData<T> {

View File

@ -11,7 +11,6 @@ import io.github.aakira.napier.Napier
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) { class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) {
@ -37,12 +36,9 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
var badgeStarred = 0 var badgeStarred = 0
set(value) {field = if (value < 0) { 0 } else { value } } set(value) {field = if (value < 0) { 0 } else { value } }
private var fetchedSources = false
private var fetchedTags = false
init { init {
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found // TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
runBlocking { CoroutineScope(Dispatchers.Main).launch {
updateApiVersion() updateApiVersion()
dateUtils = DateUtils(appSettingsService) dateUtils = DateUtils(appSettingsService)
reloadBadges() reloadBadges()
@ -52,7 +48,6 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> { suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
// TODO: Use the updatedSince parameter // TODO: Use the updatedSince parameter
var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error() var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error()
var fromDB = false
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
fetchedItems = api.getItems( fetchedItems = api.getItems(
displayedItems.type, displayedItems.type,
@ -64,7 +59,6 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
) )
} else { } else {
if (appSettingsService.isItemCachingEnabled()) { if (appSettingsService.isItemCachingEnabled()) {
fromDB = true
fetchedItems = SelfossModel.StatusAndData.succes( fetchedItems = SelfossModel.StatusAndData.succes(
getDBItems().filter { getDBItems().filter {
displayedItems == ItemType.ALL || displayedItems == ItemType.ALL ||
@ -77,9 +71,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
if (fetchedItems.success && fetchedItems.data != null) { if (fetchedItems.success && fetchedItems.data != null) {
items = ArrayList(fetchedItems.data!!) items = ArrayList(fetchedItems.data!!)
if (fromDB) { sortItems()
items.sortByDescending { dateUtils.parseDate(it.datetime) }
}
} }
return items return items
} }
@ -100,6 +92,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
if (fetchedItems.success && fetchedItems.data != null) { if (fetchedItems.success && fetchedItems.data != null) {
items.addAll(fetchedItems.data!!) items.addAll(fetchedItems.data!!)
sortItems()
} }
return items return items
} }
@ -109,9 +102,9 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
val items = api.getItems( val items = api.getItems(
itemType.type, itemType.type,
0, 0,
null, tagFilter?.tag,
null, sourceFilter?.id?.toLong(),
null, searchFilter,
null, null,
200 200
) )
@ -125,6 +118,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
} }
} }
private fun sortItems() {
items.sortByDescending { dateUtils.parseDate(it.datetime) }
}
suspend fun reloadBadges(): Boolean { suspend fun reloadBadges(): Boolean {
var success = false var success = false
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
@ -135,40 +132,32 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
badgeStarred = response.data.starred badgeStarred = response.data.starred
success = true success = true
} }
} else if (appSettingsService.isItemCachingEnabled()) { } else {
// TODO: do this differently, because it's not efficient // TODO: do this differently, because it's not efficient
val dbItems = getDBItems() val dbItems = getDBItems()
badgeUnread = dbItems.filter { item -> item.unread }.size badgeUnread = dbItems.filter { item -> item.unread }.size
badgeStarred = dbItems.filter { item -> item.starred }.size badgeStarred = dbItems.filter { item -> item.starred }.size
badgeAll = dbItems.size badgeAll = items.size
success = true
} }
return success return success
} }
suspend fun getTags(): List<SelfossModel.Tag> { suspend fun getTags(): List<SelfossModel.Tag>? {
val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() return if (isNetworkAvailable()) {
return if (isNetworkAvailable() && !fetchedTags) {
val apiTags = api.tags() val apiTags = api.tags()
if (apiTags.success && apiTags.data != null && isDatabaseEnabled) { if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
resetDBTagsWithData(apiTags.data) resetDBTagsWithData(apiTags.data)
if (!appSettingsService.isUpdateSourcesEnabled()) {
fetchedTags = true
}
} }
apiTags.data ?: emptyList() apiTags.data
} else if (isDatabaseEnabled) {
getDBTags().map { it.toView() }
} else { } else {
emptyList() getDBTags().map { it.toView() }
} }
} }
// TODO: Add tests suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
suspend fun getSpouts(): Map<String, SelfossModel.Spout> {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
val spouts = api.spouts() val spouts = api.spouts()
if (spouts.success && spouts.data != null) { return if (spouts.success && spouts.data != null) {
spouts.data spouts.data
} else { } else {
emptyMap() // TODO: do something here emptyMap() // TODO: do something here
@ -178,25 +167,18 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
} }
} }
suspend fun getSources(): ArrayList<SelfossModel.Source> { suspend fun getSources(): ArrayList<SelfossModel.Source>? {
val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() return if (isNetworkAvailable()) {
return if (isNetworkAvailable() && !fetchedSources) {
val apiSources = api.sources() val apiSources = api.sources()
if (apiSources.success && apiSources.data != null && isDatabaseEnabled) { if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
resetDBSourcesWithData(apiSources.data) resetDBSourcesWithData(apiSources.data)
if (!appSettingsService.isUpdateSourcesEnabled()) {
fetchedSources = true
}
} }
apiSources.data ?: ArrayList() apiSources.data
} else if (isDatabaseEnabled) {
ArrayList(getDBSources().map { it.toView() })
} else { } else {
ArrayList() ArrayList(getDBSources().map { it.toView() })
} }
} }
// TODO: Add tests
suspend fun markAsRead(item: SelfossModel.Item): Boolean { suspend fun markAsRead(item: SelfossModel.Item): Boolean {
val success = markAsReadById(item.id) val success = markAsReadById(item.id)
@ -208,14 +190,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun markAsReadById(id: Int): Boolean { private suspend fun markAsReadById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.markAsRead(id.toString()).isSuccess api.markAsRead(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), read = true) insertDBAction(id.toString(), read = true)
true true
} }
} }
// TODO: Add tests
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean { suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
val success = unmarkAsReadById(item.id) val success = unmarkAsReadById(item.id)
@ -227,14 +209,13 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun unmarkAsReadById(id: Int): Boolean { private suspend fun unmarkAsReadById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.unmarkAsRead(id.toString()).isSuccess api.unmarkAsRead(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), unread = true) insertDBAction(id.toString(), unread = true)
true true
} }
} }
// TODO: Add tests
suspend fun starr(item: SelfossModel.Item): Boolean { suspend fun starr(item: SelfossModel.Item): Boolean {
val success = starrById(item.id) val success = starrById(item.id)
@ -246,14 +227,13 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun starrById(id: Int): Boolean { private suspend fun starrById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.starr(id.toString()).isSuccess api.starr(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), starred = true) insertDBAction(id.toString(), starred = true)
true true
} }
} }
// TODO: Add tests
suspend fun unstarr(item: SelfossModel.Item): Boolean { suspend fun unstarr(item: SelfossModel.Item): Boolean {
val success = unstarrById(item.id) val success = unstarrById(item.id)
@ -265,18 +245,17 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private suspend fun unstarrById(id: Int): Boolean { private suspend fun unstarrById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.unstarr(id.toString()).isSuccess api.unstarr(id.toString())?.isSuccess
} else { } else {
insertDBAction(id.toString(), starred = true) insertDBAction(id.toString(), starred = true)
true true
} }
} }
// TODO: Add tests
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean { suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
var success = false var success = false
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() }).isSuccess) { if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess) {
success = true success = true
for (item in items) { for (item in items) {
markAsReadLocally(item) markAsReadLocally(item)
@ -345,7 +324,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
tags, tags,
filter, filter,
appSettingsService.getApiVersion() appSettingsService.getApiVersion()
).isSuccess == true )?.isSuccess == true
} }
return response return response
@ -355,7 +334,9 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
var success = false var success = false
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
val response = api.deleteSource(id) val response = api.deleteSource(id)
success = response.isSuccess if (response != null) {
success = response.isSuccess
}
} }
return success return success
@ -363,7 +344,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun updateRemote(): Boolean { suspend fun updateRemote(): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.update().data.equals("finished") api.update()?.equals("finished")
} else { } else {
false false
} }
@ -374,7 +355,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
try { try {
val response = api.login() val response = api.login()
result = response.isSuccess == true result = response?.isSuccess == true
if (result) { if (result) {
updateApiVersion() updateApiVersion()
} }
@ -391,7 +372,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
api.refreshLoginInformation() api.refreshLoginInformation()
} }
private suspend fun updateApiVersion() { suspend fun updateApiVersion() {
val apiMajorVersion = appSettingsService.getApiVersion() val apiMajorVersion = appSettingsService.getApiVersion()
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
@ -410,10 +391,8 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private fun deleteDBAction(action: ACTION) = private fun deleteDBAction(action: ACTION) =
db.actionsQueries.deleteAction(action.id) db.actionsQueries.deleteAction(action.id)
// TODO: This function should be private
fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList() fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList()
// TODO: This function should be private
fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList() fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) { private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
@ -452,7 +431,8 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private fun updateDBItem(item: SelfossModel.Item) = private fun updateDBItem(item: SelfossModel.Item) =
db.itemsQueries.updateItem(item.datetime, item.title.getHtmlDecoded(), item.content, item.unread, item.starred, item.thumbnail, item.icon, item.link, item.sourcetitle, item.tags.joinToString(","), item.id.toString()) db.itemsQueries.updateItem(item.datetime, item.title.getHtmlDecoded(), item.content, item.unread, item.starred, item.thumbnail, item.icon, item.link, item.sourcetitle, item.tags.joinToString(","), item.id.toString())
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item> {
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item>? {
try { try {
val newItems = getMaxItemsForBackground(ItemType.UNREAD) val newItems = getMaxItemsForBackground(ItemType.UNREAD)
val allItems = getMaxItemsForBackground(ItemType.ALL) val allItems = getMaxItemsForBackground(ItemType.ALL)
@ -465,7 +445,6 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
return emptyList() return emptyList()
} }
// TODO: Add tests
suspend fun handleDBActions() { suspend fun handleDBActions() {
val actions: List<ACTION> = getDBActions() val actions: List<ACTION> = getDBActions()

View File

@ -15,6 +15,7 @@ class AppSettingsService {
// User settings related // User settings related
private var _itemsCaching: Boolean? = null private var _itemsCaching: Boolean? = null
private var _internalBrowser: Boolean? = null
private var _articleViewer: Boolean? = null private var _articleViewer: Boolean? = null
private var _shouldBeCardView: Boolean? = null private var _shouldBeCardView: Boolean? = null
private var _displayUnreadCount: Boolean? = null private var _displayUnreadCount: Boolean? = null
@ -87,7 +88,7 @@ class AppSettingsService {
} }
fun refreshItemsNumber() { fun refreshItemsNumber() {
_itemsNumber = settings.getString("prefer_api_items_number", "20").toInt() _itemsNumber = settings.getString("prefer_api_items_number", "200").toInt()
} }
fun getApiTimeout(): Long { fun getApiTimeout(): Long {
@ -114,6 +115,17 @@ class AppSettingsService {
_password = settings.getString("password", "") _password = settings.getString("password", "")
} }
private fun refreshInternalBrowserEnabled() {
_internalBrowser = settings.getBoolean("prefer_internal_browser", true)
}
fun isInternalBrowserEnabled(): Boolean {
if (_internalBrowser != null) {
refreshInternalBrowserEnabled()
}
return _internalBrowser == true
}
private fun refreshArticleViewerEnabled() { private fun refreshArticleViewerEnabled() {
_articleViewer = settings.getBoolean("prefer_article_viewer", true) _articleViewer = settings.getBoolean("prefer_article_viewer", true)
} }
@ -328,6 +340,7 @@ class AppSettingsService {
fun refreshUserSettings() { fun refreshUserSettings() {
refreshItemsNumber() refreshItemsNumber()
refreshApiTimeout() refreshApiTimeout()
refreshInternalBrowserEnabled()
refreshArticleViewerEnabled() refreshArticleViewerEnabled()
refreshShouldBeCardViewEnabled() refreshShouldBeCardViewEnabled()
refreshDisplayUnreadCountEnabled() refreshDisplayUnreadCountEnabled()