Really big settings cleaning.

This commit is contained in:
aminecmi 2022-08-26 22:30:34 +02:00
parent 5531034086
commit a99286a9b7
23 changed files with 586 additions and 366 deletions

View File

@ -68,10 +68,6 @@
android:name=".ImageActivity"> android:name=".ImageActivity">
</activity> </activity>
<meta-data
android:name="bou.amine.apps.readerforselfossv2.android.utils.glide.SelfSignedGlideModule"
android:value="GlideModule" />
<meta-data android:name="android.webkit.WebView.MetricsOptOut" <meta-data android:name="android.webkit.WebView.MetricsOptOut"
android:value="true" /> android:value="true" />

View File

@ -12,7 +12,7 @@ import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
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.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -31,7 +31,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
override val di by closestDI() override val di by closestDI()
private val repository : Repository by instance() private val repository : Repository by instance()
private val apiDetailsService : ApiDetailsService by instance() private val appSettingsService : AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@AddSourceActivity) appColors = AppColors(this@AddSourceActivity)
@ -83,7 +83,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val baseUrl = apiDetailsService.getBaseUrl() val baseUrl = appSettingsService.getBaseUrl()
if (baseUrl.isEmpty() || !baseUrl.isBaseUrlValid(this@AddSourceActivity)) { if (baseUrl.isEmpty() || !baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
mustLoginToAddSource() mustLoginToAddSource()
} else { } else {

View File

@ -35,7 +35,7 @@ import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.* import bou.amine.apps.readerforselfossv2.utils.*
import com.ashokvarma.bottomnavigation.BottomNavigationBar import com.ashokvarma.bottomnavigation.BottomNavigationBar
import com.ashokvarma.bottomnavigation.BottomNavigationItem import com.ashokvarma.bottomnavigation.BottomNavigationItem
@ -69,7 +69,6 @@ import kotlin.concurrent.thread
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware { class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
private val MENU_PREFERENCES = 12302
private val DRAWER_ID_TAGS = 100101L private val DRAWER_ID_TAGS = 100101L
private val DRAWER_ID_HIDDEN_TAGS = 101100L private val DRAWER_ID_HIDDEN_TAGS = 101100L
private val DRAWER_ID_SOURCES = 100110L private val DRAWER_ID_SOURCES = 100110L
@ -77,21 +76,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
private var items: ArrayList<SelfossModel.Item> = ArrayList() private var items: ArrayList<SelfossModel.Item> = ArrayList()
private var internalBrowser = false
private var articleViewer = false
private var shouldBeCardView = false
private var displayUnreadCount = false
private var displayAllCount = false
private var fullHeightCards: Boolean = false
private var elementsShown: ItemType = ItemType.UNREAD private var elementsShown: ItemType = ItemType.UNREAD
private var lastFetchDone: Boolean = false private var lastFetchDone: Boolean = false
private var updateSources: Boolean = true
private var markOnScroll: Boolean = false
private var hiddenTags: List<String> = emptyList()
private var periodicRefresh = false
private var refreshMinutes: Long = 360L
private var refreshWhenChargingOnly = false
private lateinit var tabNewBadge: TextBadgeItem private lateinit var tabNewBadge: TextBadgeItem
private lateinit var tabArchiveBadge: TextBadgeItem private lateinit var tabArchiveBadge: TextBadgeItem
@ -101,7 +87,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
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
private var settings = Settings()
private lateinit var binding: ActivityHomeBinding private lateinit var binding: ActivityHomeBinding
private var recyclerAdapter: RecyclerView.Adapter<*>? = null private var recyclerAdapter: RecyclerView.Adapter<*>? = null
@ -112,6 +97,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
override val di by closestDI() override val di by closestDI()
private val repository : Repository by instance() private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
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>?)
@ -121,6 +107,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// Add appcolors to DI
appColors = AppColors(this@HomeActivity) appColors = AppColors(this@HomeActivity)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -152,8 +140,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
handleSwipeRefreshLayout() handleSwipeRefreshLayout()
handleSettings()
getElementsAccordingToTab() getElementsAccordingToTab()
@ -292,7 +278,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
// TODO: Make this the only appcolors init
appColors = AppColors(this@HomeActivity) appColors = AppColors(this@HomeActivity)
handleDrawerItems() handleDrawerItems()
@ -301,10 +286,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
reloadLayoutManager() reloadLayoutManager()
if (!settings.getBoolean("infinite_loading", false)) { if (appSettingsService.isInfiniteLoadingEnabled()) {
binding.recyclerView.setHasFixedSize(true)
} else {
handleInfiniteScroll() handleInfiniteScroll()
} else {
binding.recyclerView.setHasFixedSize(true)
} }
handleBottomBarActions() handleBottomBarActions()
@ -323,31 +308,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
customTabActivityHelper.unbindCustomTabsService(this) customTabActivityHelper.unbindCustomTabsService(this)
} }
private fun handleSettings() {
// TODO : clean this
internalBrowser = settings.getBoolean("prefer_internal_browser", true)
articleViewer = settings.getBoolean("prefer_article_viewer", true)
shouldBeCardView = settings.getBoolean("card_view_active", false)
displayUnreadCount = settings.getBoolean("display_unread_count", true)
displayAllCount = settings.getBoolean("display_other_count", false)
fullHeightCards = settings.getBoolean("full_height_cards", false)
updateSources = settings.getBoolean("update_sources", true)
markOnScroll = settings.getBoolean("mark_on_scroll", false)
hiddenTags = if (settings.getString("hidden_tags", "").isNotEmpty()) {
settings.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
} else {
emptyList()
}
periodicRefresh = settings.getBoolean("periodic_refresh", false)
refreshWhenChargingOnly = settings.getBoolean("refresh_when_charging", false)
refreshMinutes = settings.getString("periodic_refresh_minutes", "360").toLong()
if (refreshMinutes <= 15) {
refreshMinutes = 15
}
}
private fun handleThemeBinding() { private fun handleThemeBinding() {
val scoop = Scoop.getInstance() val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar) scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar)
@ -401,16 +361,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
binding.drawerContainer.addDrawerListener(drawerListener) binding.drawerContainer.addDrawerListener(drawerListener)
val displayAccountHeader =
settings.getBoolean("account_header_displaying", false)
binding.mainDrawer.addStickyFooterItem( binding.mainDrawer.addStickyFooterItem(
PrimaryDrawerItem().apply { PrimaryDrawerItem().apply {
nameRes = R.string.drawer_report_bug nameRes = R.string.drawer_report_bug
iconRes = R.drawable.ic_bug_report_black_24dp iconRes = R.drawable.ic_bug_report_black_24dp
isIconTinted = true isIconTinted = true
onDrawerItemClickListener = { _, _, _ -> onDrawerItemClickListener = { _, _, _ ->
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(ApiDetailsService.trackerUrl)) val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
startActivity(browserIntent) startActivity(browserIntent)
false false
} }
@ -427,12 +384,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
}) })
if (displayAccountHeader) { if (appSettingsService.isDisplayAccountHeaderEnabled()) {
AccountHeaderView(this).apply { AccountHeaderView(this).apply {
attachToSliderView(binding.mainDrawer) attachToSliderView(binding.mainDrawer)
addProfiles( addProfiles(
ProfileDrawerItem().apply { ProfileDrawerItem().apply {
nameText = settings.getString("url", "") nameText = appSettingsService.getBaseUrl()
setBackgroundResource(R.drawable.bg) setBackgroundResource(R.drawable.bg)
iconRes = R.mipmap.ic_launcher iconRes = R.mipmap.ic_launcher
selectionListEnabledForSingleProfile = false selectionListEnabledForSingleProfile = false
@ -496,7 +453,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} else { } else {
val filteredTags = maybeTags val filteredTags = maybeTags
.filterNot { hiddenTags.contains(it.tag) } .filterNot { appSettingsService.getHiddenTags().contains(it.tag) }
.sortedBy { it.unread == 0 } .sortedBy { it.unread == 0 }
tagsBadge = filteredTags.map { tagsBadge = filteredTags.map {
createDrawerItem(it) createDrawerItem(it)
@ -518,7 +475,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} else { } else {
val filteredHiddenTags: List<SelfossModel.Tag> = val filteredHiddenTags: List<SelfossModel.Tag> =
maybeTags.filter { hiddenTags.contains(it.tag) } maybeTags.filter { appSettingsService.getHiddenTags().contains(it.tag) }
tagsBadge = filteredHiddenTags.map { tagsBadge = filteredHiddenTags.map {
createDrawerItem(it) createDrawerItem(it)
@ -574,7 +531,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
} }
) )
if (hiddenTags.isNotEmpty()) { if (appSettingsService.getHiddenTags().isNotEmpty()) {
binding.mainDrawer.itemAdapter.add( binding.mainDrawer.itemAdapter.add(
DividerDrawerItem(), DividerDrawerItem(),
SecondaryDrawerItem().apply { SecondaryDrawerItem().apply {
@ -707,7 +664,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
// This will only update the layout manager if settings changed // This will only update the layout manager if settings changed
when (currentManager) { when (currentManager) {
is StaggeredGridLayoutManager -> is StaggeredGridLayoutManager ->
if (!shouldBeCardView) { if (!appSettingsService.isCardViewEnabled()) {
layoutManager = GridLayoutManager( layoutManager = GridLayoutManager(
this, this,
calculateNoOfColumns() calculateNoOfColumns()
@ -715,7 +672,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
binding.recyclerView.layoutManager = layoutManager binding.recyclerView.layoutManager = layoutManager
} }
is GridLayoutManager -> is GridLayoutManager ->
if (shouldBeCardView) { if (appSettingsService.isCardViewEnabled()) {
layoutManager = StaggeredGridLayoutManager( layoutManager = StaggeredGridLayoutManager(
calculateNoOfColumns(), calculateNoOfColumns(),
StaggeredGridLayoutManager.VERTICAL StaggeredGridLayoutManager.VERTICAL
@ -726,7 +683,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
else -> else ->
if (currentManager == null) { if (currentManager == null) {
if (!shouldBeCardView) { if (!appSettingsService.isCardViewEnabled()) {
layoutManager = GridLayoutManager( layoutManager = GridLayoutManager(
this, this,
calculateNoOfColumns() calculateNoOfColumns()
@ -862,15 +819,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
if (recyclerAdapter == null) { if (recyclerAdapter == null) {
if (shouldBeCardView) { if (appSettingsService.isCardViewEnabled()) {
recyclerAdapter = recyclerAdapter =
ItemCardAdapter( ItemCardAdapter(
this, this,
items, items,
customTabActivityHelper, customTabActivityHelper,
internalBrowser, // TODO remove and use from apidetailsservice
articleViewer, // TODO remove and use from apidetailsservice
fullHeightCards, // TODO remove and use from apidetailsservice
appColors, appColors,
) { ) {
updateItems(it) updateItems(it)
@ -881,8 +835,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
this, this,
items, items,
customTabActivityHelper, customTabActivityHelper,
internalBrowser, // TODO remove and use from apidetailsservice
articleViewer, // TODO remove and use from apidetailsservice
appColors, appColors,
) { ) {
updateItems(it) updateItems(it)
@ -905,7 +857,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
private fun reloadBadges() { private fun reloadBadges() {
if (displayUnreadCount || displayAllCount) { if (appSettingsService.isDisplayUnreadCountEnabled() || appSettingsService.isDisplayAllCountEnabled()) {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
repository.reloadBadges() repository.reloadBadges()
reloadBadgeContent() reloadBadgeContent()
@ -914,12 +866,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
private fun reloadBadgeContent() { private fun reloadBadgeContent() {
if (displayUnreadCount) { if (appSettingsService.isDisplayUnreadCountEnabled()) {
tabNewBadge tabNewBadge
.setText(repository.badgeUnread.toString()) .setText(repository.badgeUnread.toString())
.maybeShow() .maybeShow()
} }
if (displayAllCount) { if (appSettingsService.isDisplayAllCountEnabled()) {
tabArchiveBadge tabArchiveBadge
.setText(repository.badgeAll.toString()) .setText(repository.badgeAll.toString())
.maybeShow() .maybeShow()
@ -1038,8 +990,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
return true return true
} }
R.id.action_disconnect -> { R.id.action_disconnect -> {
val settings = Settings() appSettingsService.clearAll()
settings.clear()
val intent = Intent(this, LoginActivity::class.java) val intent = Intent(this, LoginActivity::class.java)
this.startActivity(intent) this.startActivity(intent)
this@HomeActivity.finish() this@HomeActivity.finish()
@ -1062,15 +1013,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
} }
private fun handleRecurringTask() { private fun handleRecurringTask() {
if (periodicRefresh) { if (appSettingsService.isPeriodicRefreshEnabled()) {
val myConstraints = Constraints.Builder() val myConstraints = Constraints.Builder()
.setRequiresBatteryNotLow(true) .setRequiresBatteryNotLow(true)
.setRequiresCharging(refreshWhenChargingOnly) .setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
.setRequiresStorageNotLow(true) .setRequiresStorageNotLow(true)
.build() .build()
val backgroundWork = val backgroundWork =
PeriodicWorkRequestBuilder<LoadingWorker>(refreshMinutes, TimeUnit.MINUTES) PeriodicWorkRequestBuilder<LoadingWorker>(appSettingsService.getRefreshMinutes(), TimeUnit.MINUTES)
.setConstraints(myConstraints) .setConstraints(myConstraints)
.addTag("selfoss-loading") .addTag("selfoss-loading")
.build() .build()
@ -1078,9 +1029,5 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork) WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
} }
} }
private fun handleOfflineActions() {
}
} }

View File

@ -16,8 +16,8 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBindin
import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import com.russhwolf.settings.Settings
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,19 +25,19 @@ import org.kodein.di.DIAware
import org.kodein.di.android.closestDI import org.kodein.di.android.closestDI
import org.kodein.di.instance import org.kodein.di.instance
class LoginActivity() : AppCompatActivity(), DIAware { class LoginActivity : AppCompatActivity(), DIAware {
private var inValidCount: Int = 0 private var inValidCount: Int = 0
private var isWithSelfSignedCert = false private var isWithSelfSignedCert = false
private var isWithLogin = false private var isWithLogin = false
private var isWithHTTPLogin = false private var isWithHTTPLogin = false
private val settings = Settings()
private lateinit var appColors: AppColors 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()
private val repository : Repository by instance() private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@LoginActivity) appColors = AppColors(this@LoginActivity)
@ -52,7 +52,7 @@ class LoginActivity() : AppCompatActivity(), DIAware {
handleBaseUrlFail() handleBaseUrlFail()
if (settings.getString("url", "").isNotEmpty()) { if (appSettingsService.getBaseUrl().isNotEmpty()) {
goToMain() goToMain()
} }
@ -117,11 +117,8 @@ class LoginActivity() : AppCompatActivity(), DIAware {
} }
private fun preferenceError(t: Throwable) { private fun preferenceError(t: Throwable) {
settings.remove("url") appSettingsService.resetLoginInformation()
settings.remove("login")
settings.remove("httpUserName")
settings.remove("password")
settings.remove("httpPassword")
binding.urlView.error = getString(R.string.wrong_infos) binding.urlView.error = getString(R.string.wrong_infos)
binding.loginView.error = getString(R.string.wrong_infos) binding.loginView.error = getString(R.string.wrong_infos)
binding.passwordView.error = getString(R.string.wrong_infos) binding.passwordView.error = getString(R.string.wrong_infos)
@ -199,7 +196,7 @@ class LoginActivity() : AppCompatActivity(), DIAware {
} else { } else {
showProgress(true) showProgress(true)
repository.refreshLoginInformation(url, login, password, httpLogin, httpPassword, isWithSelfSignedCert) repository.refreshLoginInformation(url, login, password, isWithSelfSignedCert)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val result = repository.login() val result = repository.login()

View File

@ -18,7 +18,7 @@ import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
import bou.amine.apps.readerforselfossv2.dao.DriverFactory import bou.amine.apps.readerforselfossv2.dao.DriverFactory
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService 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.ftinc.scoop.Scoop
@ -48,12 +48,10 @@ class MyApp : MultiDexApplication(), DIAware {
private val viewModel: AppViewModel by instance() private val viewModel: AppViewModel by instance()
private val connectivityStatus: ConnectivityStatus by instance() private val connectivityStatus: ConnectivityStatus by instance()
private val driverFactory: DriverFactory by instance() private val driverFactory: DriverFactory by instance()
private lateinit var settings : Settings
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Napier.base(DebugAntilog()) Napier.base(DebugAntilog())
settings = Settings()
initDrawerImageLoader() initDrawerImageLoader()
@ -90,11 +88,11 @@ class MyApp : MultiDexApplication(), DIAware {
val name = getString(R.string.notification_channel_sync) val name = getString(R.string.notification_channel_sync)
val importance = NotificationManager.IMPORTANCE_LOW val importance = NotificationManager.IMPORTANCE_LOW
val mChannel = NotificationChannel(ApiDetailsService.syncChannelId, name, importance) val mChannel = NotificationChannel(AppSettingsService.syncChannelId, name, importance)
val newItemsChannelname = getString(R.string.new_items_channel_sync) val newItemsChannelname = getString(R.string.new_items_channel_sync)
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
val newItemsChannelmChannel = NotificationChannel(ApiDetailsService.newItemsChannelId, newItemsChannelname, newItemsChannelimportance) val newItemsChannelmChannel = NotificationChannel(AppSettingsService.newItemsChannelId, newItemsChannelname, newItemsChannelimportance)
notificationManager.createNotificationChannel(mChannel) notificationManager.createNotificationChannel(mChannel)
notificationManager.createNotificationChannel(newItemsChannelmChannel) notificationManager.createNotificationChannel(newItemsChannelmChannel)

View File

@ -16,8 +16,8 @@ import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.android.themes.Toppings import bou.amine.apps.readerforselfossv2.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import com.russhwolf.settings.Settings
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -27,7 +27,6 @@ import org.kodein.di.instance
class ReaderActivity : AppCompatActivity(), DIAware { class ReaderActivity : AppCompatActivity(), DIAware {
private var markOnScroll: Boolean = false
private var currentItem: Int = 0 private var currentItem: Int = 0
private lateinit var appColors: AppColors private lateinit var appColors: AppColors
@ -35,12 +34,9 @@ class ReaderActivity : AppCompatActivity(), DIAware {
private lateinit var binding: ActivityReaderBinding private lateinit var binding: ActivityReaderBinding
private var activeAlignment: Int = 1
private val JUSTIFY = 1
private val ALIGN_LEFT = 2
override val di by closestDI() override val di by closestDI()
private val repository: Repository by instance() private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
private fun showMenuItem(willAddToFavorite: Boolean) { private fun showMenuItem(willAddToFavorite: Boolean) {
if (willAddToFavorite) { if (willAddToFavorite) {
@ -58,8 +54,6 @@ class ReaderActivity : AppCompatActivity(), DIAware {
showMenuItem(false) showMenuItem(false)
} }
private var settings = Settings()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
appColors = AppColors(this) appColors = AppColors(this)
@ -76,9 +70,6 @@ class ReaderActivity : AppCompatActivity(), DIAware {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
markOnScroll = settings.getBoolean("mark_on_scroll", false)
activeAlignment = settings.getInt("text_align", JUSTIFY)
if (allItems.isEmpty()) { if (allItems.isEmpty()) {
finish() finish()
} }
@ -98,10 +89,9 @@ class ReaderActivity : AppCompatActivity(), DIAware {
} }
private fun readItem(item: SelfossModel.Item) { private fun readItem(item: SelfossModel.Item) {
if (markOnScroll) { if (appSettingsService.isMarkOnScrollEnabled()) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(item) repository.markAsRead(item)
// TODO: Handle failure
} }
} }
} }
@ -142,7 +132,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
} }
private fun alignmentMenu() { private fun alignmentMenu() {
val showJustify = activeAlignment == ALIGN_LEFT val showJustify = appSettingsService.getActiveAllignment() == AppSettingsService.ALIGN_LEFT
toolbarMenu.findItem(R.id.align_left).isVisible = !showJustify toolbarMenu.findItem(R.id.align_left).isVisible = !showJustify
toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify
} }
@ -211,21 +201,19 @@ class ReaderActivity : AppCompatActivity(), DIAware {
} }
} }
R.id.align_left -> { R.id.align_left -> {
activeAlignment = ALIGN_LEFT switchAlignmentSetting(AppSettingsService.ALIGN_LEFT)
switchAlignmentSetting()
refreshFragment() refreshFragment()
} }
R.id.align_justify -> { R.id.align_justify -> {
activeAlignment = JUSTIFY switchAlignmentSetting(AppSettingsService.JUSTIFY)
switchAlignmentSetting()
refreshFragment() refreshFragment()
} }
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
private fun switchAlignmentSetting() { private fun switchAlignmentSetting(allignment: Int) {
settings.putInt("text_align", activeAlignment) appSettingsService.changeAllignment(allignment)
alignmentMenu() alignmentMenu()
} }

View File

@ -17,6 +17,7 @@ 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.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getIcon import bou.amine.apps.readerforselfossv2.utils.getIcon
import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.getThumbnail
@ -34,9 +35,6 @@ 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, private val helper: CustomTabActivityHelper,
private val internalBrowser: Boolean,
private val articleViewer: Boolean,
private val fullHeightCards: Boolean,
override val appColors: AppColors, override val appColors: AppColors,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() { ) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
@ -47,6 +45,7 @@ class ItemCardAdapter(
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 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)
@ -66,7 +65,7 @@ class ItemCardAdapter(
binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils) binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils)
if (!fullHeightCards) { if (!appSettingsService.isFullHeightCardsEnabled()) {
binding.itemImage.maxHeight = imageMaxHeight binding.itemImage.maxHeight = imageMaxHeight
binding.itemImage.scaleType = ScaleType.CENTER_CROP binding.itemImage.scaleType = ScaleType.CENTER_CROP
} }
@ -146,8 +145,8 @@ class ItemCardAdapter(
bindingAdapterPosition, bindingAdapterPosition,
items[bindingAdapterPosition].getLinkDecoded(), items[bindingAdapterPosition].getLinkDecoded(),
customTabsIntent, customTabsIntent,
internalBrowser, appSettingsService.isInternalBrowserEnabled(),
articleViewer, appSettingsService.isArticleViewerEnabled(),
app app
) )
} }

View File

@ -14,6 +14,7 @@ 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.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getIcon import bou.amine.apps.readerforselfossv2.utils.getIcon
import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.getThumbnail
@ -27,8 +28,6 @@ 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, private val helper: CustomTabActivityHelper,
private val internalBrowser: Boolean,
private val articleViewer: Boolean,
override val appColors: AppColors, override val appColors: AppColors,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
) : ItemsAdapter<ItemListAdapter.ViewHolder>() { ) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
@ -37,6 +36,7 @@ class ItemListAdapter(
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 fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
@ -94,8 +94,8 @@ class ItemListAdapter(
bindingAdapterPosition, bindingAdapterPosition,
items[bindingAdapterPosition].getLinkDecoded(), items[bindingAdapterPosition].getLinkDecoded(),
customTabsIntent, customTabsIntent,
internalBrowser, appSettingsService.isInternalBrowserEnabled(),
articleViewer, appSettingsService.isArticleViewerEnabled(),
app app
) )
} }

View File

@ -8,6 +8,7 @@ import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.AppColors
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.ItemType import bou.amine.apps.readerforselfossv2.utils.ItemType
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -18,6 +19,7 @@ import org.kodein.di.DIAware
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>(), DIAware { abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>(), DIAware {
abstract var items: ArrayList<SelfossModel.Item> abstract var items: ArrayList<SelfossModel.Item>
abstract val repository: Repository abstract val repository: Repository
abstract val appSettingsService: AppSettingsService
abstract val app: Activity abstract val app: Activity
abstract val appColors: AppColors abstract val appColors: AppColors
abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
@ -92,8 +94,6 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
private fun unreadItemAtIndex(position: Int, showSnackbar: Boolean = true) { private fun unreadItemAtIndex(position: Int, showSnackbar: Boolean = true) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.unmarkAsRead(items[position]) repository.unmarkAsRead(items[position])
// Todo: SharedItems.unreadItem(app, api, db, items[position])
// TODO: update db
} }
notifyItemChanged(position) notifyItemChanged(position)

View File

@ -17,7 +17,7 @@ import bou.amine.apps.readerforselfossv2.android.model.preloadImages
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible
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.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -31,33 +31,32 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
override val di by lazy { (applicationContext as MyApp).di } override val di by lazy { (applicationContext as MyApp).di }
private val repository : Repository by instance() private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
override fun doWork(): Result { override fun doWork(): Result {
val settings = Settings() if (appSettingsService.isPeriodicRefreshEnabled() && isNetworkAccessible(context)) {
val periodicRefresh = settings.getBoolean("periodic_refresh", false)
if (periodicRefresh && isNetworkAccessible(context)) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val notificationManager = val notificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification = val notification =
NotificationCompat.Builder(applicationContext, ApiDetailsService.syncChannelId) NotificationCompat.Builder(applicationContext, AppSettingsService.syncChannelId)
.setContentTitle(context.getString(R.string.loading_notification_title)) .setContentTitle(context.getString(R.string.loading_notification_title))
.setContentText(context.getString(R.string.loading_notification_text)) .setContentText(context.getString(R.string.loading_notification_text))
.setOngoing(true) .setOngoing(true)
.setPriority(PRIORITY_LOW) .setPriority(PRIORITY_LOW)
.setChannelId(ApiDetailsService.syncChannelId) .setChannelId(AppSettingsService.syncChannelId)
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp) .setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
notificationManager.notify(1, notification.build()) notificationManager.notify(1, notification.build())
val notifyNewItems = settings.getBoolean("notify_new_items", false)
repository.handleDBActions() repository.handleDBActions()
if (appSettingsService.isNotifyNewItemsEnabled()) {
launch { launch {
handleNewItemsNotification(repository.tryToCacheItemsAndGetNewOnes(), notifyNewItems, notificationManager) handleNewItemsNotification(repository.tryToCacheItemsAndGetNewOnes(), notificationManager)
}
} }
} }
} }
@ -66,7 +65,6 @@ override fun doWork(): Result {
private fun handleNewItemsNotification( private fun handleNewItemsNotification(
newItems: List<SelfossModel.Item>?, newItems: List<SelfossModel.Item>?,
notifyNewItems: Boolean,
notificationManager: NotificationManager notificationManager: NotificationManager
) { ) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@ -74,7 +72,7 @@ override fun doWork(): Result {
val newSize = apiItems.filter { it.unread }.size val newSize = apiItems.filter { it.unread }.size
if (notifyNewItems && newSize > 0) { if (newSize > 0) {
val intent = Intent(context, MainActivity::class.java).apply { val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
@ -87,7 +85,7 @@ override fun doWork(): Result {
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, pflags) val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, pflags)
val newItemsNotification = val newItemsNotification =
NotificationCompat.Builder(applicationContext, ApiDetailsService.newItemsChannelId) NotificationCompat.Builder(applicationContext, AppSettingsService.newItemsChannelId)
.setContentTitle(context.getString(R.string.new_items_notification_title)) .setContentTitle(context.getString(R.string.new_items_notification_title))
.setContentText( .setContentText(
context.getString( context.getString(
@ -96,7 +94,7 @@ override fun doWork(): Result {
) )
) )
.setPriority(PRIORITY_DEFAULT) .setPriority(PRIORITY_DEFAULT)
.setChannelId(ApiDetailsService.newItemsChannelId) .setChannelId(AppSettingsService.newItemsChannelId)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setAutoCancel(true) .setAutoCancel(true)
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp) .setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)

View File

@ -31,6 +31,7 @@ import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActiv
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getImages import bou.amine.apps.readerforselfossv2.utils.getImages
import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.getThumbnail
@ -74,8 +75,7 @@ class ArticleFragment : Fragment(), DIAware {
override val di : DI by closestDI() override val di : DI by closestDI()
private val repository: Repository by instance() private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
private var settings = Settings()
private var typeface: Typeface? = null private var typeface: Typeface? = null
private var resId: Int = 0 private var resId: Int = 0
@ -114,10 +114,10 @@ class ArticleFragment : Fragment(), DIAware {
contentSource = item.sourceAndDateText(repository.dateUtils) contentSource = item.sourceAndDateText(repository.dateUtils)
allImages = item.getImages() allImages = item.getImages()
fontSize = settings.getString("reader_font_size", "16").toInt() fontSize = appSettingsService.getFontSize()
staticBar = settings.getBoolean("reader_static_bar", false) staticBar = appSettingsService.isStaticBarEnabled()
font = appSettingsService.getFont()
font = settings.getString("reader_font", "")
if (font.isNotEmpty()) { if (font.isNotEmpty()) {
resId = requireContext().resources.getIdentifier(font, "font", requireContext().packageName) resId = requireContext().resources.getIdentifier(font, "font", requireContext().packageName)
typeface = try { typeface = try {
@ -239,7 +239,7 @@ class ArticleFragment : Fragment(), DIAware {
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title)) .setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
.setPositiveButton(android.R.string.ok .setPositiveButton(android.R.string.ok
) { _, _ -> ) { _, _ ->
settings.putBoolean("prefer_article_viewer", false) appSettingsService.disableArticleViewer()
requireActivity().finish() requireActivity().finish()
} }
.create() .create()
@ -255,7 +255,7 @@ class ArticleFragment : Fragment(), DIAware {
} }
private fun refreshAlignment() { private fun refreshAlignment() {
textAlignment = when (settings.getInt("text_align", 1)) { textAlignment = when (appSettingsService.getActiveAllignment()) {
1 -> "justify" 1 -> "justify"
2 -> "left" 2 -> "left"
else -> "justify" else -> "justify"

View File

@ -14,8 +14,9 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
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.android.themes.Toppings
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import java.lang.NumberFormatException import java.lang.NumberFormatException
@ -174,12 +175,7 @@ class SettingsActivity : AppCompatActivity(),
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId val id = item.itemId
if (id == R.id.clear) { if (id == R.id.clear) {
val settings = Settings() AppColors.resetColors()
settings.remove("color_primary")
settings.remove("color_primary_dark")
settings.remove("color_accent")
settings.remove("color_accent_dark")
settings.remove("dark_theme")
requireActivity().recreate() requireActivity().recreate()
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
@ -196,17 +192,17 @@ class SettingsActivity : AppCompatActivity(),
setPreferencesFromResource(R.xml.pref_links, rootKey) setPreferencesFromResource(R.xml.pref_links, rootKey)
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
openUrl(Uri.parse(ApiDetailsService.trackerUrl)) openUrl(Uri.parse(AppSettingsService.trackerUrl))
true true
} }
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
openUrl(Uri.parse(ApiDetailsService.sourceUrl)) openUrl(Uri.parse(AppSettingsService.sourceUrl))
false false
} }
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
openUrl(Uri.parse(ApiDetailsService.translationUrl)) openUrl(Uri.parse(AppSettingsService.translationUrl))
false false
} }
} }

View File

@ -58,4 +58,15 @@ class AppColors(a: Activity) {
a.resources.getColor(R.color.grey_900) 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

@ -1,33 +0,0 @@
package bou.amine.apps.readerforselfossv2.android.utils.glide
import android.content.Context
import bou.amine.apps.readerforselfossv2.android.utils.getUnsafeHttpClient
import com.bumptech.glide.Glide
import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.Registry
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.module.GlideModule
import com.russhwolf.settings.Settings
import java.io.InputStream
class SelfSignedGlideModule : GlideModule {
override fun applyOptions(context: Context?, builder: GlideBuilder?) {
}
override fun registerComponents(context: Context?, glide: Glide?, registry: Registry?) {
if (context != null) {
val settings = Settings()
if (settings.getBoolean("isSelfSignedCert", false)) {
val client = getUnsafeHttpClient().build()
registry?.append(
GlideUrl::class.java,
InputStream::class.java,
com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader.Factory(client)
)
}
}
}
}

View File

@ -1,15 +1,15 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
import android.text.format.DateUtils import android.text.format.DateUtils
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import java.time.Instant import java.time.Instant
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.ZoneOffset import java.time.ZoneOffset
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
actual class DateUtils actual constructor(apiDetailsService: ApiDetailsService) { actual class DateUtils actual constructor(appSettingsService: AppSettingsService) {
val ads: ApiDetailsService = apiDetailsService // TODO: why is this needed now ? val ads: AppSettingsService = appSettingsService // TODO: why is this needed now ?
actual fun parseDate(dateString: String): Long { actual fun parseDate(dateString: String): Long {

View File

@ -1,13 +1,13 @@
package bou.amine.apps.readerforselfossv2.DI package bou.amine.apps.readerforselfossv2.DI
import bou.amine.apps.readerforselfossv2.rest.SelfossApi import bou.amine.apps.readerforselfossv2.rest.SelfossApi
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.bind import org.kodein.di.bind
import org.kodein.di.instance import org.kodein.di.instance
import org.kodein.di.singleton import org.kodein.di.singleton
val networkModule by DI.Module { val networkModule by DI.Module {
bind<ApiDetailsService>() with singleton { ApiDetailsService() } bind<AppSettingsService>() with singleton { AppSettingsService() }
bind<SelfossApi>() with singleton { SelfossApi(instance()) } bind<SelfossApi>() with singleton { SelfossApi(instance()) }
} }

View File

@ -4,7 +4,7 @@ import bou.amine.apps.readerforselfossv2.dao.*
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
import bou.amine.apps.readerforselfossv2.rest.SelfossApi import bou.amine.apps.readerforselfossv2.rest.SelfossApi
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.* import bou.amine.apps.readerforselfossv2.utils.*
import com.github.ln_12.library.ConnectivityStatus import com.github.ln_12.library.ConnectivityStatus
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
@ -13,14 +13,13 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService, private val connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) { class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) {
val settings = Settings()
var items = ArrayList<SelfossModel.Item>() var items = ArrayList<SelfossModel.Item>()
val isConnectionAvailable = connectivityStatus.isNetworkConnected val isConnectionAvailable = connectivityStatus.isNetworkConnected
var connectionMonitored = false var connectionMonitored = false
var baseUrl = apiDetails.getBaseUrl() var baseUrl = appSettingsService.getBaseUrl()
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
var displayedItems = ItemType.UNREAD var displayedItems = ItemType.UNREAD
@ -29,7 +28,6 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
var sourceFilter: SelfossModel.Source? = null var sourceFilter: SelfossModel.Source? = null
var searchFilter: String? = null var searchFilter: String? = null
var itemsCaching = settings.getBoolean("items_caching", false)
var offlineOverride = false var offlineOverride = false
var badgeUnread = 0 var badgeUnread = 0
@ -43,7 +41,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
// 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
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
updateApiVersion() updateApiVersion()
dateUtils = DateUtils(apiDetails) dateUtils = DateUtils(appSettingsService)
reloadBadges() reloadBadges()
} }
} }
@ -61,7 +59,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
null null
) )
} else { } else {
if (itemsCaching) { if (appSettingsService.isItemCachingEnabled()) {
fetchedItems = getDBItems().filter { fetchedItems = getDBItems().filter {
displayedItems == ItemType.ALL || displayedItems == ItemType.ALL ||
(it.unread && displayedItems == ItemType.UNREAD) || (it.unread && displayedItems == ItemType.UNREAD) ||
@ -98,7 +96,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return items return items
} }
suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item>? { private suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item>? {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.getItems( api.getItems(
itemType.type, itemType.type,
@ -172,7 +170,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun markAsReadById(id: Int): Boolean { private suspend fun markAsReadById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.markAsRead(id.toString())?.isSuccess == true api.markAsRead(id.toString())?.isSuccess == true
} else { } else {
@ -191,7 +189,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun unmarkAsReadById(id: Int): Boolean { private suspend fun unmarkAsReadById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.unmarkAsRead(id.toString())?.isSuccess == true api.unmarkAsRead(id.toString())?.isSuccess == true
} else { } else {
@ -209,7 +207,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun starrById(id: Int): Boolean { private suspend fun starrById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.starr(id.toString())?.isSuccess == true api.starr(id.toString())?.isSuccess == true
} else { } else {
@ -227,7 +225,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return success return success
} }
suspend fun unstarrById(id: Int): Boolean { private suspend fun unstarrById(id: Int): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.unstarr(id.toString())?.isSuccess == true api.unstarr(id.toString())?.isSuccess == true
} else { } else {
@ -307,7 +305,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
spout, spout,
tags, tags,
filter, filter,
apiDetails.getApiVersion() appSettingsService.getApiVersion()
)?.isSuccess == true )?.isSuccess == true
} }
@ -350,27 +348,19 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
return result return result
} }
fun refreshLoginInformation(url: String, login: String, password: String, fun refreshLoginInformation(url: String, login: String, password: String, isSelfSignedCert: Boolean) {
httpLogin: String, httpPassword: String, appSettingsService.refreshLoginInformation(url, login, password, isSelfSignedCert)
isSelfSignedCert: Boolean) {
settings.putString("url", url)
settings.putString("login", login)
settings.putString("httpUserName", httpLogin)
settings.putString("password", password)
settings.putString("httpPassword", httpPassword)
settings.putBoolean("isSelfSignedCert", isSelfSignedCert)
baseUrl = url baseUrl = url
api.refreshLoginInformation() api.refreshLoginInformation()
} }
private suspend fun updateApiVersion() { private suspend fun updateApiVersion() {
val apiMajorVersion = apiDetails.getApiVersion() val apiMajorVersion = appSettingsService.getApiVersion()
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
val fetchedVersion = api.version() val fetchedVersion = api.version()
if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) { if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) {
settings.putInt("apiVersionMajor", fetchedVersion.getApiMajorVersion()) appSettingsService.updateApiVersion(fetchedVersion.getApiMajorVersion())
apiDetails.refreshApiVersion()
} }
} }
dateUtils = DateUtils(apiMajorVersion) dateUtils = DateUtils(apiMajorVersion)
@ -378,10 +368,10 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride
fun getDBActions(): List<ACTION> = private fun getDBActions(): List<ACTION> =
db.actionsQueries.actions().executeAsList() db.actionsQueries.actions().executeAsList()
fun deleteDBAction(action: ACTION) = private fun deleteDBAction(action: ACTION) =
db.actionsQueries.deleteAction(action.id) db.actionsQueries.deleteAction(action.id)
fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList() fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList()
@ -432,7 +422,9 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
val starredItems = getMaxItemsForBackground(ItemType.STARRED) val starredItems = getMaxItemsForBackground(ItemType.STARRED)
insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty()) insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty())
return newItems return newItems
} catch (e: Throwable) {} } catch (e: Throwable) {
// We do nothing
}
return emptyList() return emptyList()
} }

View File

@ -1,7 +1,7 @@
package bou.amine.apps.readerforselfossv2.rest package bou.amine.apps.readerforselfossv2.rest
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.plugins.* import io.ktor.client.plugins.*
@ -14,7 +14,7 @@ import io.ktor.client.plugins.logging.*
import io.ktor.serialization.kotlinx.json.* import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
class SelfossApi(private val apiDetailsService: ApiDetailsService) { class SelfossApi(private val appSettingsService: AppSettingsService) {
var client = createHttpClient() var client = createHttpClient()
@ -31,13 +31,13 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
install(Logging) { install(Logging) {
logger = object : Logger { logger = object : Logger {
override fun log(message: String) { override fun log(message: String) {
apiDetailsService.logApiCalls(message) appSettingsService.logApiCalls(message)
} }
} }
level = LogLevel.ALL level = LogLevel.ALL
} }
install(HttpTimeout) { install(HttpTimeout) {
requestTimeoutMillis = apiDetailsService.getApiTimeout() requestTimeoutMillis = appSettingsService.getApiTimeout()
} }
/* TODO: Auth as basic /* TODO: Auth as basic
if (apiDetailsService.getUserName().isNotEmpty() && apiDetailsService.getPassword().isNotEmpty()) { if (apiDetailsService.getUserName().isNotEmpty() && apiDetailsService.getPassword().isNotEmpty()) {
@ -58,17 +58,17 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
} }
fun url(path: String) = fun url(path: String) =
"${apiDetailsService.getBaseUrl()}$path" "${appSettingsService.getBaseUrl()}$path"
fun refreshLoginInformation() { fun refreshLoginInformation() {
apiDetailsService.refresh() appSettingsService.refreshApiSettings()
client = createHttpClient() client = createHttpClient()
} }
suspend fun login(): SelfossModel.SuccessResponse? = suspend fun login(): SelfossModel.SuccessResponse? =
client.get(url("/login")) { client.get(url("/login")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun getItems( suspend fun getItems(
@ -81,45 +81,45 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
items: Int? = null items: Int? = null
): List<SelfossModel.Item>? = ): List<SelfossModel.Item>? =
client.get(url("/items")) { client.get(url("/items")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
parameter("type", type) parameter("type", type)
parameter("tag", tag) parameter("tag", tag)
parameter("source", source) parameter("source", source)
parameter("search", search) parameter("search", search)
parameter("updatedsince", updatedSince) parameter("updatedsince", updatedSince)
parameter("items", items ?: apiDetailsService.getItemsNumber()) parameter("items", items ?: appSettingsService.getItemsNumber())
parameter("offset", offset) parameter("offset", offset)
}.body() }.body()
suspend fun stats(): SelfossModel.Stats? = suspend fun stats(): SelfossModel.Stats? =
client.get(url("/stats")) { client.get(url("/stats")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun tags(): List<SelfossModel.Tag>? = suspend fun tags(): List<SelfossModel.Tag>? =
client.get(url("/tags")) { client.get(url("/tags")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun update(): String? = suspend fun update(): String? =
client.get(url("/update")) { client.get(url("/update")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun spouts(): Map<String, SelfossModel.Spout>? = suspend fun spouts(): Map<String, SelfossModel.Spout>? =
client.get(url("/sources/spouts")) { client.get(url("/sources/spouts")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun sources(): ArrayList<SelfossModel.Source>? = suspend fun sources(): ArrayList<SelfossModel.Source>? =
client.get(url("/sources/list")) { client.get(url("/sources/list")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun version(): SelfossModel.ApiVersion? = suspend fun version(): SelfossModel.ApiVersion? =
@ -127,34 +127,34 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? = suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? =
client.post(url("/mark/$id")) { client.post(url("/mark/$id")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? = suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? =
client.post(url("/unmark/$id")) { client.post(url("/unmark/$id")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun starr(id: String): SelfossModel.SuccessResponse? = suspend fun starr(id: String): SelfossModel.SuccessResponse? =
client.post(url("/starr/$id")) { client.post(url("/starr/$id")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun unstarr(id: String): SelfossModel.SuccessResponse? = suspend fun unstarr(id: String): SelfossModel.SuccessResponse? =
client.post(url("/unstarr/$id")) { client.post(url("/unstarr/$id")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse? = suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse? =
client.submitForm( client.submitForm(
url = url("/mark"), url = url("/mark"),
formParameters = Parameters.build { formParameters = Parameters.build {
append("username", apiDetailsService.getUserName()) append("username", appSettingsService.getUserName())
append("password", apiDetailsService.getPassword()) append("password", appSettingsService.getPassword())
ids.map { append("ids[]", it) } ids.map { append("ids[]", it) }
} }
).body() ).body()
@ -181,7 +181,7 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
filter: String filter: String
): SelfossModel.SuccessResponse? = ): SelfossModel.SuccessResponse? =
client.submitForm( client.submitForm(
url = url("/source?username=${apiDetailsService.getUserName()}&password=${apiDetailsService.getPassword()}"), url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
formParameters = Parameters.build { formParameters = Parameters.build {
append("title", title) append("title", title)
append("url", url) append("url", url)
@ -199,7 +199,7 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
filter: String filter: String
): SelfossModel.SuccessResponse? = ): SelfossModel.SuccessResponse? =
client.submitForm( client.submitForm(
url = url("/source?username=${apiDetailsService.getUserName()}&password=${apiDetailsService.getPassword()}"), url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
formParameters = Parameters.build { formParameters = Parameters.build {
append("title", title) append("title", title)
append("url", url) append("url", url)
@ -211,7 +211,7 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse? = suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse? =
client.delete(url("/source/$id")) { client.delete(url("/source/$id")) {
parameter("username", apiDetailsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", apiDetailsService.getPassword()) parameter("password", appSettingsService.getPassword())
}.body() }.body()
} }

View File

@ -1,97 +0,0 @@
package bou.amine.apps.readerforselfossv2.service
import com.russhwolf.settings.Settings
import io.github.aakira.napier.Napier
import io.ktor.client.plugins.*
class ApiDetailsService {
val settings: Settings = Settings()
private var _apiVersion: Int = -1
private var _baseUrl: String = ""
private var _userName: String = ""
private var _password: String = ""
private var _itemsNumber: Int? = null
private var _apiTimeout: Long? = null
fun logApiCalls(message: String) {
Napier.d(message, tag = "LogApiCalls")
}
fun getApiVersion(): Int {
if (_apiVersion == -1) {
refreshApiVersion()
return _apiVersion
}
return _apiVersion
}
fun refreshApiVersion() {
_apiVersion = settings.getInt("apiVersionMajor", -1)
}
fun getBaseUrl(): String {
if (_baseUrl.isEmpty()) {
_baseUrl = settings.getString("url", "")
}
return _baseUrl
}
fun getUserName(): String {
if (_userName.isEmpty()) {
_userName = settings.getString("login", "")
}
return _userName
}
fun getPassword(): String {
if (_password.isEmpty()) {
_password = settings.getString("password", "")
}
return _password
}
fun getItemsNumber(): Int {
if (_itemsNumber == null) {
refreshItemsNumber()
}
return _itemsNumber!!
}
fun refreshItemsNumber() {
_itemsNumber = settings.getString("prefer_api_items_number", "200").toInt()
}
fun getApiTimeout(): Long {
if (_apiTimeout == null) {
refreshApiTimeout()
}
return _apiTimeout!!
}
fun refreshApiTimeout() {
val settingsTimeout = settings.getLong("api_timeout", HttpTimeout.INFINITE_TIMEOUT_MS)
_apiTimeout = if (settingsTimeout > 0) settingsTimeout else HttpTimeout.INFINITE_TIMEOUT_MS
}
fun refresh() {
_password = settings.getString("password", "")
_userName = settings.getString("login", "")
_baseUrl = settings.getString("url", "")
refreshApiVersion()
refreshItemsNumber()
refreshApiTimeout()
}
companion object {
const val translationUrl = "https://crwd.in/readerforselfoss"
const val sourceUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform"
const val trackerUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues"
const val syncChannelId = "sync-channel-id"
const val newItemsChannelId = "new-items-channel-id"
}
}

View File

@ -0,0 +1,428 @@
package bou.amine.apps.readerforselfossv2.service
import com.russhwolf.settings.Settings
import io.github.aakira.napier.Napier
import io.ktor.client.plugins.*
class AppSettingsService {
val settings: Settings = Settings()
// Api related
private var _apiVersion: Int = -1
private var _baseUrl: String = ""
private var _userName: String = ""
private var _password: String = ""
// User settings related
private var _itemsCaching: Boolean? = null
private var _internalBrowser: Boolean? = null
private var _articleViewer: Boolean? = null
private var _shouldBeCardView: Boolean? = null
private var _displayUnreadCount: Boolean? = null
private var _displayAllCount: Boolean? = null
private var _fullHeightCards: Boolean? = null
private var _updateSources: Boolean? = null
private var _periodicRefresh: Boolean? = null
private var _refreshWhenChargingOnly: Boolean? = null
private var _infiniteLoading: Boolean? = null
private var _displayAccountHeader: Boolean? = null
private var _notifyNewItems: Boolean? = null
private var _itemsNumber: Int? = null
private var _apiTimeout: Long? = null
private var _hiddenTags: List<String>? = null
private var _refreshMinutes: Long = 360
private var _markOnScroll: Boolean? = null
private var _activeAlignment: Int? = null
private var _fontSize: Int? = null // settings.getString("reader_font_size", "16").toInt()
private var _staticBar: Boolean? = null // settings.getBoolean("reader_static_bar", false)
private var _font: String = "" // settings.getString("reader_font", "")
init {
refreshApiSettings()
refreshUserSettings()
}
fun logApiCalls(message: String) {
Napier.d(message, tag = "LogApiCalls")
}
fun getApiVersion(): Int {
if (_apiVersion == -1) {
refreshApiVersion()
return _apiVersion
}
return _apiVersion
}
fun refreshApiVersion() {
_apiVersion = settings.getInt("apiVersionMajor", -1)
}
fun getBaseUrl(): String {
if (_baseUrl.isEmpty()) {
refreshBaseUrl()
}
return _baseUrl
}
fun getUserName(): String {
if (_userName.isEmpty()) {
refrershUsername()
}
return _userName
}
fun getPassword(): String {
if (_password.isEmpty()) {
refreshPassword()
}
return _password
}
fun getItemsNumber(): Int {
if (_itemsNumber == null) {
refreshItemsNumber()
}
return _itemsNumber!!
}
fun refreshItemsNumber() {
_itemsNumber = settings.getString("prefer_api_items_number", "200").toInt()
}
fun getApiTimeout(): Long {
if (_apiTimeout == null) {
refreshApiTimeout()
}
return _apiTimeout!!
}
private fun refreshApiTimeout() {
val settingsTimeout = settings.getLong("api_timeout", HttpTimeout.INFINITE_TIMEOUT_MS)
_apiTimeout = if (settingsTimeout > 0) settingsTimeout else HttpTimeout.INFINITE_TIMEOUT_MS
}
private fun refreshBaseUrl() {
_baseUrl = settings.getString("url", "")
}
private fun refrershUsername() {
_userName = settings.getString("login", "")
}
private fun refreshPassword() {
_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() {
_articleViewer = settings.getBoolean("prefer_article_viewer", true)
}
fun isArticleViewerEnabled(): Boolean {
if (_articleViewer != null) {
refreshArticleViewerEnabled()
}
return _articleViewer == true
}
private fun refreshShouldBeCardViewEnabled() {
_shouldBeCardView = settings.getBoolean("card_view_active", false)
}
fun isCardViewEnabled(): Boolean {
if (_shouldBeCardView != null) {
refreshShouldBeCardViewEnabled()
}
return _shouldBeCardView == true
}
private fun refreshDisplayUnreadCountEnabled() {
_displayUnreadCount = settings.getBoolean("display_unread_count", true)
}
fun isDisplayUnreadCountEnabled(): Boolean {
if (_displayUnreadCount != null) {
refreshDisplayUnreadCountEnabled()
}
return _displayUnreadCount == true
}
private fun refreshDisplayAllCountEnabled() {
_displayAllCount = settings.getBoolean("display_other_count", false)
}
fun isDisplayAllCountEnabled(): Boolean {
if (_displayAllCount != null) {
refreshDisplayAllCountEnabled()
}
return _displayAllCount == true
}
private fun refreshFullHeightCardsEnabled() {
_fullHeightCards = settings.getBoolean("full_height_cards", false)
}
fun isFullHeightCardsEnabled(): Boolean {
if (_fullHeightCards != null) {
refreshFullHeightCardsEnabled()
}
return _fullHeightCards == true
}
private fun refreshUpdateSourcesEnabled() {
_updateSources = settings.getBoolean("update_sources", true)
}
fun isUpdateSourcesEnabled(): Boolean {
if (_updateSources != null) {
refreshUpdateSourcesEnabled()
}
return _updateSources == true
}
private fun refreshPeriodicRefreshEnabled() {
_periodicRefresh = settings.getBoolean("periodic_refresh", false)
}
fun isPeriodicRefreshEnabled(): Boolean {
if (_periodicRefresh != null) {
refreshPeriodicRefreshEnabled()
}
return _periodicRefresh == true
}
private fun refreshRefreshWhenChargingOnlyEnabled() {
_refreshWhenChargingOnly = settings.getBoolean("refresh_when_charging", false)
}
fun isRefreshWhenChargingOnlyEnabled(): Boolean {
if (_refreshWhenChargingOnly != null) {
refreshRefreshWhenChargingOnlyEnabled()
}
return _refreshWhenChargingOnly == true
}
private fun refreshRefreshMinutes() {
_refreshMinutes = settings.getString("periodic_refresh_minutes", "360").toLong()
if (_refreshMinutes <= 15) {
_refreshMinutes = 15
}
}
fun getRefreshMinutes(): Long {
if (_refreshMinutes != null) {
refreshRefreshMinutes()
}
return _refreshMinutes
}
private fun refreshHiddenTags() {
if (settings.getString("hidden_tags", "").isNotEmpty()) {
_hiddenTags = settings.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
}
}
fun getHiddenTags(): List<String> {
if (_hiddenTags != null) {
refreshHiddenTags()
}
return _hiddenTags.orEmpty()
}
private fun refreshInfiniteLoadingEnabled() {
_infiniteLoading = settings.getBoolean("infinite_loading", false)
}
fun isInfiniteLoadingEnabled(): Boolean {
if (_infiniteLoading != null) {
refreshInfiniteLoadingEnabled()
}
return _infiniteLoading == true
}
private fun refreshDisplayAccountHeaderEnabled() {
_displayAccountHeader = settings.getBoolean("account_header_displaying", false)
}
fun isDisplayAccountHeaderEnabled(): Boolean {
if (_displayAccountHeader != null) {
refreshDisplayAccountHeaderEnabled()
}
return _displayAccountHeader == true
}
private fun refreshItemCachingEnabled() {
_itemsCaching = settings.getBoolean("items_caching", false)
}
fun isItemCachingEnabled(): Boolean {
if (_itemsCaching != null) {
refreshItemCachingEnabled()
}
return _itemsCaching == true
}
private fun refreshNotifyNewItemsEnabled() {
_notifyNewItems = settings.getBoolean("notify_new_items", false)
}
fun isNotifyNewItemsEnabled(): Boolean {
if (_notifyNewItems != null) {
refreshNotifyNewItemsEnabled()
}
return _notifyNewItems == true
}
private fun refreshMarkOnScrollEnabled() {
_markOnScroll = settings.getBoolean("mark_on_scroll", false)
}
fun isMarkOnScrollEnabled(): Boolean {
if (_markOnScroll != null) {
refreshMarkOnScrollEnabled()
}
return _markOnScroll == true
}
private fun refreshActiveAllignment() {
_activeAlignment = settings.getInt("text_align", JUSTIFY)
}
fun getActiveAllignment(): Int {
if (_activeAlignment != null) {
refreshActiveAllignment()
}
return _activeAlignment ?: JUSTIFY
}
fun changeAllignment(allignment: Int) {
settings.putInt("text_align", allignment)
_activeAlignment = allignment
}
private fun refreshFontSize() {
_fontSize = settings.getString("reader_font_size", "16").toInt()
}
fun getFontSize(): Int {
if (_fontSize != null) {
refreshFontSize()
}
return _fontSize ?: 16
}
private fun refreshStaticBarEnabled() {
_staticBar = settings.getBoolean("reader_static_bar", false)
}
fun isStaticBarEnabled(): Boolean {
if (_staticBar != null) {
refreshStaticBarEnabled()
}
return _staticBar == true
}
private fun refreshFont() {
_font = settings.getString("reader_font", "")
}
fun getFont(): String {
if (_font != null) {
refreshFont()
}
return _font
}
fun refreshApiSettings() {
refreshPassword()
refrershUsername()
refreshBaseUrl()
refreshApiVersion()
}
fun refreshUserSettings() {
refreshItemsNumber()
refreshApiTimeout()
refreshInternalBrowserEnabled()
refreshArticleViewerEnabled()
refreshShouldBeCardViewEnabled()
refreshDisplayUnreadCountEnabled()
refreshDisplayAllCountEnabled()
refreshFullHeightCardsEnabled()
refreshUpdateSourcesEnabled()
refreshPeriodicRefreshEnabled()
refreshRefreshWhenChargingOnlyEnabled()
refreshRefreshMinutes()
refreshHiddenTags()
refreshInfiniteLoadingEnabled()
refreshDisplayAccountHeaderEnabled()
refreshItemCachingEnabled()
refreshNotifyNewItemsEnabled()
refreshMarkOnScrollEnabled()
refreshActiveAllignment()
refreshFontSize()
refreshFont()
refreshStaticBarEnabled()
}
fun refreshLoginInformation(
url: String,
login: String,
password: String,
selfSignedCert: Boolean
) {
settings.putString("url", url)
settings.putString("login", login)
settings.putString("password", password)
settings.putBoolean("isSelfSignedCert", selfSignedCert)
refreshApiSettings()
}
fun resetLoginInformation() {
settings.remove("url")
settings.remove("login")
settings.remove("password")
refreshApiSettings()
}
fun updateApiVersion(apiMajorVersion: Int) {
settings.putInt("apiVersionMajor", apiMajorVersion)
refreshApiVersion()
}
fun clearAll() {
settings.clear()
refreshApiSettings()
refreshUserSettings()
}
fun disableArticleViewer() {
settings.putBoolean("prefer_article_viewer", false)
refreshArticleViewerEnabled()
}
companion object {
const val translationUrl = "https://crwd.in/readerforselfoss"
const val sourceUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform"
const val trackerUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues"
const val syncChannelId = "sync-channel-id"
const val newItemsChannelId = "new-items-channel-id"
const val JUSTIFY = 1
const val ALIGN_LEFT = 2
}
}

View File

@ -1,13 +1,13 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
fun SelfossModel.Item.parseDate(dateUtils: DateUtils): Long = fun SelfossModel.Item.parseDate(dateUtils: DateUtils): Long =
dateUtils.parseDate(this.datetime) dateUtils.parseDate(this.datetime)
expect class DateUtils(apiDetailsService: ApiDetailsService) { expect class DateUtils(appSettingsService: AppSettingsService) {
fun parseDate(dateString: String): Long fun parseDate(dateString: String): Long
fun parseRelativeDate(dateString: String): String fun parseRelativeDate(dateString: String): String

View File

@ -1,8 +1,8 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
actual class DateUtils actual constructor(apiDetailsService: ApiDetailsService) { actual class DateUtils actual constructor(appSettingsService: AppSettingsService) {
actual fun parseDate(dateString: String): Long { actual fun parseDate(dateString: String): Long {
TODO("Not yet implemented") TODO("Not yet implemented")
} }

View File

@ -1,8 +1,8 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
actual class DateUtils actual constructor(apiDetailsService: ApiDetailsService) { actual class DateUtils actual constructor(appSettingsService: AppSettingsService) {
actual fun parseDate(dateString: String): Long { actual fun parseDate(dateString: String): Long {
TODO("Not yet implemented") TODO("Not yet implemented")
} }