From 137580ccf9de738292848a13f52c2c59a50f0f54 Mon Sep 17 00:00:00 2001 From: aminecmi Date: Thu, 12 Oct 2023 20:25:24 +0200 Subject: [PATCH] chore: lint cleaning. --- .drone.yml | 4 +- .../readerforselfossv2/android/ACRAUtils.kt | 2 +- .../android/HomeActivity.kt | 315 +++---- .../android/ImageActivity.kt | 58 +- .../android/LoginActivity.kt | 46 +- .../android/MainActivity.kt | 1 - .../apps/readerforselfossv2/android/MyApp.kt | 110 +-- .../android/ReaderActivity.kt | 15 +- .../android/SourcesActivity.kt | 13 +- .../android/UpsertSourceActivity.kt | 73 +- .../android/adapters/ItemCardAdapter.kt | 15 +- .../android/adapters/ItemListAdapter.kt | 20 +- .../android/adapters/ItemsAdapter.kt | 70 +- .../android/adapters/SourcesListAdapter.kt | 19 +- .../android/background/background.kt | 35 +- .../android/fragments/ArticleFragment.kt | 268 +++--- .../android/fragments/FilterSheetFragment.kt | 58 +- .../android/fragments/ImageFragment.kt | 23 +- .../android/model/AndroidIModelUtils.kt | 9 +- .../android/model/ParecelableItem.kt | 32 +- .../android/settings/SettingsActivity.kt | 190 +++-- .../android/utils/AppUtils.kt | 11 +- .../android/utils/CircleImageView.kt | 93 ++- .../android/utils/LinksUtils.kt | 13 +- .../android/utils/bottombar/BottomBarUtils.kt | 3 +- .../android/utils/glide/GlideUtils.kt | 28 +- .../android/utils/network/NetworkUtils.kt | 2 +- .../android/viewmodel/AppViewModel.kt | 11 +- androidApp/src/test/kotlin/DatesTest.kt | 10 +- androidApp/src/test/kotlin/RepositoryTest.kt | 326 ++++---- androidApp/src/test/kotlin/TestUtils.kt | 11 +- .../readerforselfossv2/dao/DeviceDatabase.kt | 2 +- .../readerforselfossv2/rest/NetworkSSL.kt | 12 +- .../readerforselfossv2/utils/DateUtils.kt | 34 +- .../utils/ModelConverters.kt | 14 +- .../apps/readerforselfossv2/DI/modules.kt | 3 +- .../readerforselfossv2/dao/DeviceDatabase.kt | 2 +- .../readerforselfossv2/model/MercuryModel.kt | 3 +- .../model/NetworkUnavailableException.kt | 2 +- .../readerforselfossv2/model/ResultModel.kt | 2 +- .../readerforselfossv2/model/SelfossModel.kt | 47 +- .../repository/RepositoryImpl.kt | 147 ++-- .../readerforselfossv2/rest/MercuryApi.kt | 32 +- .../apps/readerforselfossv2/rest/RestUtils.kt | 39 +- .../readerforselfossv2/rest/SelfossApi.kt | 786 +++++++++++------- .../service/ACRASettings.kt | 63 +- .../service/AppSettingsService.kt | 63 +- .../readerforselfossv2/utils/EntityUtils.kt | 14 +- .../apps/readerforselfossv2/utils/Enums.kt | 5 +- .../utils/ModelConverters.kt | 6 +- .../readerforselfossv2/utils/StringUtils.kt | 5 +- .../readerforselfossv2/dao/DriverFactory.kt | 2 +- .../rest/setupInsecureHTTPEngine.kt | 2 +- .../readerforselfossv2/utils/DateUtils.kt | 2 +- .../utils/ModelConverters.kt | 8 +- .../readerforselfossv2/dao/DriverFactory.kt | 2 +- .../rest/setupInsecureHTTPEngine.kt | 2 +- .../readerforselfossv2/utils/DateUtils.kt | 5 +- .../utils/ModelConverters.kt | 8 +- 59 files changed, 1820 insertions(+), 1376 deletions(-) diff --git a/.drone.yml b/.drone.yml index 42bd819..e3da259 100644 --- a/.drone.yml +++ b/.drone.yml @@ -13,10 +13,10 @@ steps: - curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.1/detekt-cli-1.23.1.zip && unzip detekt-cli-1.23.1.zip - echo "---------------------------------------------------------" - echo "Linting..." - - ktlint || true + - ktlint 'shared/**/*.kt' 'androidApp/**/*.kt' '!shared/build' || true - echo "---------------------------------------------------------" - echo "Detecting..." - - ./detekt-cli-1.23.1/bin/detekt-cli --all-rules || true + - ./detekt-cli-1.23.1/bin/detekt-cli --all-rules --excludes '**/shared/build/**/*.kt' || true - echo "---------------------------------------------------------" command_timeout: 1m - name: BuildAndTest diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ACRAUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ACRAUtils.kt index 1a93b6a..14ebf0e 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ACRAUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ACRAUtils.kt @@ -6,4 +6,4 @@ import org.acra.ktx.sendSilentlyWithAcra fun Throwable.sendSilentlyWithAcraWithName(name: String) { ACRA.errorReporter.putCustomData("error_source", name) this.sendSilentlyWithAcra() -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt index 3a2a84f..116d21b 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt @@ -43,9 +43,7 @@ import org.kodein.di.instance import java.security.MessageDigest import java.util.concurrent.TimeUnit - class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware { - private var items: ArrayList = ArrayList() private var elementsShown: ItemType = ItemType.UNREAD @@ -63,22 +61,22 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar private var fromTabShortcut: Boolean = false - private val settingsLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - appSettingsService.refreshUserSettings() - } + private val settingsLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + appSettingsService.refreshUserSettings() + } override val di by closestDI() - private val repository : Repository by instance() - private val appSettingsService : AppSettingsService by instance() - + private val repository: Repository by instance() + private val appSettingsService: AppSettingsService by instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityHomeBinding.inflate(layoutInflater) val view = binding.root - fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1 - repository.offlineOverride = intent.getBooleanExtra("startOffline", false) + fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1 + repository.offlineOverride = intent.getBooleanExtra("startOffline", false) if (fromTabShortcut) { elementsShown = ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position)) @@ -92,7 +90,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar handleSwipeRefreshLayout() - if (appSettingsService.isItemCachingEnabled()) { CoroutineScope(Dispatchers.Main).launch { repository.tryToCacheItemsAndGetNewOnes() @@ -104,7 +101,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar binding.swipeRefreshLayout.setColorSchemeResources( R.color.refresh_progress_1, R.color.refresh_progress_2, - R.color.refresh_progress_3 + R.color.refresh_progress_3, ) binding.swipeRefreshLayout.setOnRefreshListener { repository.offlineOverride = false @@ -115,37 +112,41 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar } } - val swipeDirs = if (appSettingsService.getPublicAccess()) { - 0 - } else { - ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT - } + val swipeDirs = + if (appSettingsService.getPublicAccess()) { + 0 + } else { + ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT + } val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback( 0, - swipeDirs + swipeDirs, ) { override fun getSwipeDirs( recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder + viewHolder: RecyclerView.ViewHolder, ): Int = if (elementsShown == ItemType.STARRED) { 0 } else { super.getSwipeDirs( recyclerView, - viewHolder + viewHolder, ) } override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder + target: RecyclerView.ViewHolder, ): Boolean = false - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) { + override fun onSwiped( + viewHolder: RecyclerView.ViewHolder, + swipeDir: Int, + ) { val position = viewHolder.bindingAdapterPosition val i = items.elementAtOrNull(position) @@ -162,7 +163,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar Toast.makeText( this@HomeActivity, "Found null when swiping at positon $position.", - Toast.LENGTH_LONG + Toast.LENGTH_LONG, ).show() } } @@ -171,7 +172,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(binding.recyclerView) } - private fun updateBottomBarBadgeCount(badge: TextBadgeItem, count: Int) { + private fun updateBottomBarBadgeCount( + badge: TextBadgeItem, + count: Int, + ) { if (count > 0) { badge .setText(count.toString()) @@ -182,16 +186,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar } private fun handleBottomBar() { - - tabNewBadge = TextBadgeItem() - .setText("") - .setHideOnSelect(false).hide(false) - tabArchiveBadge = TextBadgeItem() - .setText("") - .setHideOnSelect(false).hide(false) - tabStarredBadge = TextBadgeItem() - .setText("") - .setHideOnSelect(false).hide(false) + tabNewBadge = + TextBadgeItem() + .setText("") + .setHideOnSelect(false).hide(false) + tabArchiveBadge = + TextBadgeItem() + .setText("") + .setHideOnSelect(false).hide(false) + tabStarredBadge = + TextBadgeItem() + .setText("") + .setHideOnSelect(false).hide(false) if (appSettingsService.isDisplayUnreadCountEnabled()) { lifecycleScope.launch { @@ -218,19 +224,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar val tabNew = BottomNavigationItem( R.drawable.ic_tab_fiber_new_black_24dp, - getString(R.string.tab_new) + getString(R.string.tab_new), ) .setBadgeItem(tabNewBadge) val tabArchive = BottomNavigationItem( R.drawable.ic_tab_archive_black_24dp, - getString(R.string.tab_read) + getString(R.string.tab_read), ) .setBadgeItem(tabArchiveBadge) val tabStarred = BottomNavigationItem( R.drawable.ic_tab_favorite_black_24dp, - getString(R.string.tab_favs) + getString(R.string.tab_favs), ).setActiveColorResource(R.color.pink) .setBadgeItem(tabStarredBadge) @@ -271,7 +277,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar getElementsAccordingToTab() } - private fun handleGDPRDialog(GDPRShown: Boolean) { val messageDigest: MessageDigest = MessageDigest.getInstance("SHA-256") messageDigest.update(appSettingsService.getBaseUrl().toByteArray()) @@ -281,7 +286,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar alertDialog.setMessage(getString(R.string.gdpr_dialog_message)) alertDialog.setButton( AlertDialog.BUTTON_NEUTRAL, - "OK" + "OK", ) { dialog, _ -> appSettingsService.settings.putBoolean("GDPR_shown", true) dialog.dismiss() @@ -298,37 +303,41 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar when (currentManager) { is StaggeredGridLayoutManager -> if (!appSettingsService.isCardViewEnabled()) { - layoutManager = GridLayoutManager( - this, - calculateNoOfColumns() - ) + layoutManager = + GridLayoutManager( + this, + calculateNoOfColumns(), + ) binding.recyclerView.layoutManager = layoutManager } is GridLayoutManager -> if (appSettingsService.isCardViewEnabled()) { - layoutManager = StaggeredGridLayoutManager( - calculateNoOfColumns(), - StaggeredGridLayoutManager.VERTICAL - ) + layoutManager = + StaggeredGridLayoutManager( + calculateNoOfColumns(), + StaggeredGridLayoutManager.VERTICAL, + ) layoutManager.gapStrategy = - StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS + StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS binding.recyclerView.layoutManager = layoutManager } else -> if (currentManager == null) { if (!appSettingsService.isCardViewEnabled()) { - layoutManager = GridLayoutManager( - this, - calculateNoOfColumns() - ) + layoutManager = + GridLayoutManager( + this, + calculateNoOfColumns(), + ) binding.recyclerView.layoutManager = layoutManager } else { - layoutManager = StaggeredGridLayoutManager( - calculateNoOfColumns(), - StaggeredGridLayoutManager.VERTICAL - ) + layoutManager = + StaggeredGridLayoutManager( + calculateNoOfColumns(), + StaggeredGridLayoutManager.VERTICAL, + ) layoutManager.gapStrategy = - StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS + StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS binding.recyclerView.layoutManager = layoutManager } } @@ -336,39 +345,40 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar } private fun handleBottomBarActions() { - binding.bottomBar.setTabSelectedListener(object : BottomNavigationBar.OnTabSelectedListener { - override fun onTabUnselected(position: Int) = Unit + binding.bottomBar.setTabSelectedListener( + object : BottomNavigationBar.OnTabSelectedListener { + override fun onTabUnselected(position: Int) = Unit - override fun onTabReselected(position: Int) { - - when (val layoutManager = binding.recyclerView.adapter) { - is StaggeredGridLayoutManager -> - if (layoutManager.findFirstCompletelyVisibleItemPositions(null)[0] == 0) { - getElementsAccordingToTab() - } else { - layoutManager.scrollToPositionWithOffset(0, 0) - } - is GridLayoutManager -> - if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) { - getElementsAccordingToTab() - } else { - layoutManager.scrollToPositionWithOffset(0, 0) - } - else -> Unit + override fun onTabReselected(position: Int) { + when (val layoutManager = binding.recyclerView.adapter) { + is StaggeredGridLayoutManager -> + if (layoutManager.findFirstCompletelyVisibleItemPositions(null)[0] == 0) { + getElementsAccordingToTab() + } else { + layoutManager.scrollToPositionWithOffset(0, 0) + } + is GridLayoutManager -> + if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) { + getElementsAccordingToTab() + } else { + layoutManager.scrollToPositionWithOffset(0, 0) + } + else -> Unit + } } - } - override fun onTabSelected(position: Int) { - offset = 0 - lastFetchDone = false + override fun onTabSelected(position: Int) { + offset = 0 + lastFetchDone = false - elementsShown = ItemType.fromInt(position + 1) - getElementsAccordingToTab() - binding.recyclerView.scrollToPosition(0) + elementsShown = ItemType.fromInt(position + 1) + getElementsAccordingToTab() + binding.recyclerView.scrollToPosition(0) - fetchOnEmptyList() - } - }) + fetchOnEmptyList() + } + }, + ) } fun fetchOnEmptyList() { @@ -378,27 +388,33 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar } private fun handleInfiniteScroll() { - recyclerViewScrollListener = object : RecyclerView.OnScrollListener() { - override fun onScrolled(localRecycler: RecyclerView, dx: Int, dy: Int) { - if (dy > 0) { - val lastVisibleItem = getLastVisibleItem() + recyclerViewScrollListener = + object : RecyclerView.OnScrollListener() { + override fun onScrolled( + localRecycler: RecyclerView, + dx: Int, + dy: Int, + ) { + if (dy > 0) { + val lastVisibleItem = getLastVisibleItem() - if (lastVisibleItem == (items.size - 1) && items.size < maxItemNumber()) { - getElementsAccordingToTab(appendResults = true) + if (lastVisibleItem == (items.size - 1) && items.size < maxItemNumber()) { + getElementsAccordingToTab(appendResults = true) + } } } } - } binding.recyclerView.clearOnScrollListeners() binding.recyclerView.addOnScrollListener(recyclerViewScrollListener) } - private fun getLastVisibleItem() : Int { + private fun getLastVisibleItem(): Int { return when (val manager = binding.recyclerView.layoutManager) { - is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions( - null - ).last() + is StaggeredGridLayoutManager -> + manager.findLastCompletelyVisibleItemPositions( + null, + ).last() is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition() else -> 0 } @@ -411,28 +427,31 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar binding.emptyText.visibility = View.GONE } - fun getElementsAccordingToTab( - appendResults: Boolean = false - ) { - offset = if (appendResults && items.size > 0) { - items.size - 1 - } else { - 0 - } + fun getElementsAccordingToTab(appendResults: Boolean = false) { + offset = + if (appendResults && items.size > 0) { + items.size - 1 + } else { + 0 + } firstVisible = if (appendResults) firstVisible else 0 getItems(appendResults, elementsShown) } - private fun getItems(appendResults: Boolean, itemType: ItemType) { + private fun getItems( + appendResults: Boolean, + itemType: ItemType, + ) { CoroutineScope(Dispatchers.Main).launch { binding.swipeRefreshLayout.isRefreshing = true repository.displayedItems = itemType - items = if (appendResults) { - repository.getOlderItems() - } else { - repository.getNewerItems() - } + items = + if (appendResults) { + repository.getOlderItems() + } else { + repository.getNewerItems() + } binding.swipeRefreshLayout.isRefreshing = false handleListResult() } @@ -441,43 +460,44 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar private fun handleListResult(appendResults: Boolean = false) { if (appendResults) { val oldManager = binding.recyclerView.layoutManager - firstVisible = when (oldManager) { - is StaggeredGridLayoutManager -> - oldManager.findFirstCompletelyVisibleItemPositions(null).last() - is GridLayoutManager -> - oldManager.findFirstCompletelyVisibleItemPosition() - else -> 0 - } + firstVisible = + when (oldManager) { + is StaggeredGridLayoutManager -> + oldManager.findFirstCompletelyVisibleItemPositions(null).last() + is GridLayoutManager -> + oldManager.findFirstCompletelyVisibleItemPosition() + else -> 0 + } } if (recyclerAdapter == null) { if (appSettingsService.isCardViewEnabled()) { recyclerAdapter = - ItemCardAdapter( - this, - items, - ) { - updateItems(it) - } + ItemCardAdapter( + this, + items, + ) { + updateItems(it) + } } else { recyclerAdapter = - ItemListAdapter( - this, - items, - ) { - updateItems(it) - } + ItemListAdapter( + this, + items, + ) { + updateItems(it) + } binding.recyclerView.addItemDecoration( DividerItemDecoration( this@HomeActivity, - DividerItemDecoration.VERTICAL - ) + DividerItemDecoration.VERTICAL, + ), ) } binding.recyclerView.adapter = recyclerAdapter } else { - (recyclerAdapter as ItemsAdapter<*>).updateAllItems(items) + (recyclerAdapter as ItemsAdapter<*>).updateAllItems(items) } reloadBadges() @@ -529,7 +549,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar return true } - private fun needsConfirmation(titleRes: Int, messageRes: Int, doFn: () -> Unit) { + private fun needsConfirmation( + titleRes: Int, + messageRes: Int, + doFn: () -> Unit, + ) { AlertDialog.Builder(this@HomeActivity) .setMessage(messageRes) .setTitle(titleRes) @@ -554,14 +578,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar if (updatedRemote) { Toast.makeText( this@HomeActivity, - R.string.refresh_success_response, Toast.LENGTH_LONG + R.string.refresh_success_response, + Toast.LENGTH_LONG, ) .show() } else { Toast.makeText( this@HomeActivity, R.string.refresh_failer_message, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } @@ -579,7 +604,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar Toast.makeText( this@HomeActivity, R.string.all_posts_read, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() tabNewBadge.removeBadge() @@ -588,7 +613,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar Toast.makeText( this@HomeActivity, R.string.all_posts_not_read, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } handleListResult() @@ -635,11 +660,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar private fun handleRecurringTask() { if (appSettingsService.isPeriodicRefreshEnabled()) { - val myConstraints = Constraints.Builder() - .setRequiresBatteryNotLow(true) - .setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled()) - .setRequiresStorageNotLow(true) - .build() + val myConstraints = + Constraints.Builder() + .setRequiresBatteryNotLow(true) + .setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled()) + .setRequiresStorageNotLow(true) + .build() val backgroundWork = PeriodicWorkRequestBuilder(appSettingsService.getRefreshMinutes(), TimeUnit.MINUTES) @@ -647,8 +673,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar .addTag("selfoss-loading") .build() - WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork) + WorkManager.getInstance( + baseContext, + ).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork) } } } - diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ImageActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ImageActivity.kt index c566ab9..6221b6e 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ImageActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ImageActivity.kt @@ -11,8 +11,8 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityImageBindin import bou.amine.apps.readerforselfossv2.android.fragments.ImageFragment class ImageActivity : AppCompatActivity() { - private lateinit var allImages : ArrayList - private var position : Int = 0 + private lateinit var allImages: ArrayList + private var position: Int = 0 private lateinit var binding: ActivityImageBinding @@ -32,27 +32,44 @@ class ImageActivity : AppCompatActivity() { binding.pager.adapter = ScreenSlidePagerAdapter(this) binding.pager.setCurrentItem(position, false) - val transitionListener = object : MotionLayout.TransitionListener { - override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) { - // Nothing - } + val transitionListener = + object : MotionLayout.TransitionListener { + override fun onTransitionStarted( + motionLayout: MotionLayout?, + startId: Int, + endId: Int, + ) { + // Nothing + } - override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float - ) { - // Nothing - } + override fun onTransitionChange( + motionLayout: MotionLayout?, + startId: Int, + endId: Int, + progress: Float, + ) { + // Nothing + } - override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) { - if (motionLayout?.currentState == binding.root.endState) { - onBackPressedDispatcher.onBackPressed() - overridePendingTransition(0, 0) + override fun onTransitionCompleted( + motionLayout: MotionLayout?, + currentId: Int, + ) { + if (motionLayout?.currentState == binding.root.endState) { + onBackPressedDispatcher.onBackPressed() + overridePendingTransition(0, 0) + } + } + + override fun onTransitionTrigger( + motionLayout: MotionLayout?, + triggerId: Int, + positive: Boolean, + progress: Float, + ) { + // Nothing } } - - override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) { - // Nothing - } - } binding.root.setTransitionListener(transitionListener) } @@ -68,9 +85,8 @@ class ImageActivity : AppCompatActivity() { } private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { - override fun getItemCount(): Int = allImages.size override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position]) } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt index 5e337f9..722f142 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt @@ -28,9 +28,7 @@ import org.kodein.di.DIAware import org.kodein.di.android.closestDI import org.kodein.di.instance - class LoginActivity : AppCompatActivity(), DIAware { - private var inValidCount: Int = 0 private var isWithLogin = false @@ -40,7 +38,6 @@ class LoginActivity : AppCompatActivity(), DIAware { private val repository: Repository by instance() private val appSettingsService: AppSettingsService by instance() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -69,7 +66,6 @@ class LoginActivity : AppCompatActivity(), DIAware { } private fun handleActions() { - binding.passwordView.setOnEditorActionListener( TextView.OnEditorActionListener { _, id, _ -> if (id == R.id.loginView || id == EditorInfo.IME_NULL) { @@ -77,7 +73,7 @@ class LoginActivity : AppCompatActivity(), DIAware { return@OnEditorActionListener true } false - } + }, ) binding.signInButton.setOnClickListener { attemptLogin() } @@ -98,7 +94,7 @@ class LoginActivity : AppCompatActivity(), DIAware { alertDialog.setMessage(getString(R.string.base_url_error)) alertDialog.setButton( AlertDialog.BUTTON_NEUTRAL, - "OK" + "OK", ) { dialog, _ -> dialog.dismiss() } alertDialog.show() } @@ -123,7 +119,6 @@ class LoginActivity : AppCompatActivity(), DIAware { } private fun attemptLogin() { - // Reset errors. binding.urlView.error = null binding.loginView.error = null @@ -155,7 +150,7 @@ class LoginActivity : AppCompatActivity(), DIAware { Toast.makeText( applicationContext, R.string.application_selfoss_only, - Toast.LENGTH_LONG + Toast.LENGTH_LONG, ).show() } preferenceError() @@ -169,7 +164,7 @@ class LoginActivity : AppCompatActivity(), DIAware { private fun failLoginDetails( password: String, - login: String + login: String, ) { var lastFocusedView: View? = null var cancel = false @@ -202,7 +197,7 @@ class LoginActivity : AppCompatActivity(), DIAware { alertDialog.setMessage(getString(R.string.text_wrong_url)) alertDialog.setButton( AlertDialog.BUTTON_NEUTRAL, - "OK" + "OK", ) { dialog, _ -> dialog.dismiss() } alertDialog.show() inValidCount = 0 @@ -211,7 +206,10 @@ class LoginActivity : AppCompatActivity(), DIAware { maybeCancelAndFocusView(cancel, focusView) } - private fun maybeCancelAndFocusView(cancel: Boolean, focusView: View?) { + private fun maybeCancelAndFocusView( + cancel: Boolean, + focusView: View?, + ) { if (cancel) { focusView?.requestFocus() } @@ -225,12 +223,13 @@ class LoginActivity : AppCompatActivity(), DIAware { .animate() .setDuration(shortAnimTime.toLong()) .alpha( - if (show) 0F else 1F - ).setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE - } - } + if (show) 0F else 1F, + ).setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE + } + }, ) binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE @@ -238,12 +237,13 @@ class LoginActivity : AppCompatActivity(), DIAware { .animate() .setDuration(shortAnimTime.toLong()) .alpha( - if (show) 1F else 0F - ).setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE - } - } + if (show) 1F else 0F, + ).setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE + } + }, ) } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MainActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MainActivity.kt index 7e08951..5800146 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MainActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MainActivity.kt @@ -8,7 +8,6 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt index a4a4d38..8dc3388 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt @@ -32,20 +32,20 @@ import org.acra.sender.HttpSender import org.kodein.di.* class MyApp : MultiDexApplication(), DIAware { - override val di by DI.lazy { bind() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess()) } import(networkModule) bind() with singleton { DriverFactory(applicationContext) } bind() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) } - bind() with singleton { - Repository( - instance(), - instance(), - isConnectionAvailable, - instance() - ) - } + bind() with + singleton { + Repository( + instance(), + instance(), + isConnectionAvailable, + instance(), + ) + } bind() with singleton { ConnectivityStatus(applicationContext) } bind() with singleton { AppViewModel(repository = instance()) } } @@ -70,23 +70,24 @@ class MyApp : MultiDexApplication(), DIAware { ProcessLifecycleOwner.get().lifecycle.addObserver( AppLifeCycleObserver( connectivityStatus, - repository - ) + repository, + ), ) CoroutineScope(Dispatchers.Main).launch { viewModel.networkAvailableProvider.collect { networkAvailable -> - val toastMessage = if (networkAvailable) { - repository.handleDBActions() - R.string.network_connectivity_retrieved - } else { - R.string.network_connectivity_lost - } + val toastMessage = + if (networkAvailable) { + repository.handleDBActions() + R.string.network_connectivity_retrieved + } else { + R.string.network_connectivity_lost + } Toast.makeText( applicationContext, toastMessage, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } @@ -100,37 +101,38 @@ class MyApp : MultiDexApplication(), DIAware { initAcra { reportFormat = StringFormat.JSON - reportContent = listOf( - ReportField.REPORT_ID, - ReportField.INSTALLATION_ID, - ReportField.APP_VERSION_CODE, - ReportField.APP_VERSION_NAME, - ReportField.BUILD, - ReportField.ANDROID_VERSION, - ReportField.BRAND, - ReportField.PHONE_MODEL, - ReportField.AVAILABLE_MEM_SIZE, - ReportField.TOTAL_MEM_SIZE, - ReportField.STACK_TRACE, - ReportField.APPLICATION_LOG, - ReportField.LOGCAT, - ReportField.INITIAL_CONFIGURATION, - ReportField.CRASH_CONFIGURATION, - ReportField.IS_SILENT, - ReportField.USER_APP_START_DATE, - ReportField.USER_COMMENT, - ReportField.USER_CRASH_DATE, - ReportField.USER_EMAIL, - ReportField.CUSTOM_DATA - ) + reportContent = + listOf( + ReportField.REPORT_ID, + ReportField.INSTALLATION_ID, + ReportField.APP_VERSION_CODE, + ReportField.APP_VERSION_NAME, + ReportField.BUILD, + ReportField.ANDROID_VERSION, + ReportField.BRAND, + ReportField.PHONE_MODEL, + ReportField.AVAILABLE_MEM_SIZE, + ReportField.TOTAL_MEM_SIZE, + ReportField.STACK_TRACE, + ReportField.APPLICATION_LOG, + ReportField.LOGCAT, + ReportField.INITIAL_CONFIGURATION, + ReportField.CRASH_CONFIGURATION, + ReportField.IS_SILENT, + ReportField.USER_APP_START_DATE, + ReportField.USER_COMMENT, + ReportField.USER_CRASH_DATE, + ReportField.USER_EMAIL, + ReportField.CUSTOM_DATA, + ) toast { - //required + // required text = getString(R.string.crash_toast_text) length = Toast.LENGTH_SHORT } httpSender { uri = - "https://bugs.amine-louveau.fr/report" /*best guess, you may need to adjust this*/ + "https://bugs.amine-louveau.fr/report" // best guess, you may need to adjust this basicAuthLogin = "qMEscjj89Gwt6cPR" basicAuthPassword = "Yo58QFlGzFaWlBzP" httpMethod = HttpSender.Method.POST @@ -148,11 +150,12 @@ class MyApp : MultiDexApplication(), DIAware { val newItemsChannelname = getString(R.string.new_items_channel_sync) val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT - val newItemsChannelmChannel = NotificationChannel( - AppSettingsService.newItemsChannelId, - newItemsChannelname, - newItemsChannelimportance - ) + val newItemsChannelmChannel = + NotificationChannel( + AppSettingsService.newItemsChannelId, + newItemsChannelname, + newItemsChannelimportance, + ) notificationManager.createNotificationChannel(mChannel) notificationManager.createNotificationChannel(newItemsChannelmChannel) @@ -163,9 +166,11 @@ class MyApp : MultiDexApplication(), DIAware { val oldHandler = Thread.getDefaultUncaughtExceptionHandler() Thread.setDefaultUncaughtExceptionHandler { thread, e -> - if (e is NoClassDefFoundError && e.stackTrace.asList().any { + if (e is NoClassDefFoundError && + e.stackTrace.asList().any { it.toString().contains("android.view.ViewDebug") - }) { + } + ) { // Nothing } else { oldHandler.uncaughtException(thread, e) @@ -175,9 +180,8 @@ class MyApp : MultiDexApplication(), DIAware { class AppLifeCycleObserver( val connectivityStatus: ConnectivityStatus, - val repository: Repository + val repository: Repository, ) : DefaultLifecycleObserver { - override fun onResume(owner: LifecycleOwner) { super.onResume(owner) repository.connectionMonitored = true @@ -190,4 +194,4 @@ class MyApp : MultiDexApplication(), DIAware { super.onPause(owner) } } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt index d2f9da5..a736528 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt @@ -23,7 +23,6 @@ import org.kodein.di.android.closestDI import org.kodein.di.instance class ReaderActivity : AppCompatActivity(), DIAware { - private var currentItem: Int = 0 private lateinit var toolbarMenu: Menu @@ -102,15 +101,15 @@ class ReaderActivity : AppCompatActivity(), DIAware { private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { - override fun getItemCount(): Int = allItems.size - override fun createFragment(position: Int): Fragment = - ArticleFragment.newInstance(allItems[position]) - + override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position]) } - override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + override fun onKeyDown( + keyCode: Int, + event: KeyEvent?, + ): Boolean { return when (keyCode) { KeyEvent.KEYCODE_VOLUME_DOWN -> { val currentFragment = @@ -152,10 +151,8 @@ class ReaderActivity : AppCompatActivity(), DIAware { canFavorite() } - binding.pager.registerOnPageChangeCallback( object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { super.onPageSelected(position) @@ -166,7 +163,7 @@ class ReaderActivity : AppCompatActivity(), DIAware { } readItem(allItems[position]) } - } + }, ) } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt index ca47080..b655848 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt @@ -18,11 +18,10 @@ import org.kodein.di.android.closestDI import org.kodein.di.instance class SourcesActivity : AppCompatActivity(), DIAware { - private lateinit var binding: ActivitySourcesBinding override val di by closestDI() - private val repository : Repository by instance() + private val repository: Repository by instance() override fun onCreate(savedInstanceState: Bundle?) { binding = ActivitySourcesBinding.inflate(layoutInflater) @@ -58,16 +57,18 @@ class SourcesActivity : AppCompatActivity(), DIAware { val response = repository.getSourcesDetails() if (response.isNotEmpty()) { items = response - val mAdapter = SourcesListAdapter( - this@SourcesActivity, items - ) + val mAdapter = + SourcesListAdapter( + this@SourcesActivity, + items, + ) binding.recyclerView.adapter = mAdapter mAdapter.notifyDataSetChanged() } else { Toast.makeText( this@SourcesActivity, R.string.cant_get_sources, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt index c24babe..a6b93d8 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt @@ -21,9 +21,7 @@ import org.kodein.di.DIAware import org.kodein.di.android.closestDI import org.kodein.di.instance - class UpsertSourceActivity : AppCompatActivity(), DIAware { - private var existingSource: SelfossModel.SourceDetail? = null private var mSpoutsValue: String? = null @@ -58,7 +56,6 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware { supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.title = resources.getString(title) - maybeGetDetailsFromIntentSharing(intent) binding.saveBtn.setOnClickListener { @@ -88,25 +85,30 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware { private fun handleSpoutsSpinner() { val spoutsKV = HashMap() - binding.spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(adapterView: AdapterView<*>, view: View?, i: Int, l: Long) { - if (view != null) { - val spoutName = (view as TextView).text.toString() - mSpoutsValue = spoutsKV[spoutName] + binding.spoutsSpinner.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + adapterView: AdapterView<*>, + view: View?, + i: Int, + l: Long, + ) { + if (view != null) { + val spoutName = (view as TextView).text.toString() + mSpoutsValue = spoutsKV[spoutName] + } + } + + override fun onNothingSelected(adapterView: AdapterView<*>) { + mSpoutsValue = null } } - override fun onNothingSelected(adapterView: AdapterView<*>) { - mSpoutsValue = null - } - } - - fun handleSpoutFailure(networkIssue: Boolean = false) { Toast.makeText( this@UpsertSourceActivity, if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() binding.progress.visibility = View.GONE } @@ -127,7 +129,7 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware { ArrayAdapter( this@UpsertSourceActivity, android.R.layout.simple_spinner_item, - itemsStrings + itemsStrings, ) spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) binding.spoutsSpinner.adapter = spinnerArrayAdapter @@ -144,9 +146,7 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware { } } - private fun maybeGetDetailsFromIntentSharing( - intent: Intent - ) { + private fun maybeGetDetailsFromIntentSharing(intent: Intent) { if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) { binding.sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT)) binding.nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE)) @@ -172,29 +172,30 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware { } else -> { CoroutineScope(Dispatchers.Main).launch { - val successfullyAddedSource = if (existingSource != null) { - repository.updateSource( - existingSource!!.id, - binding.nameInput.text.toString(), - url, - mSpoutsValue!!, - binding.tags.text.toString() - ) - } else { - repository.createSource( - binding.nameInput.text.toString(), - url, - mSpoutsValue!!, - binding.tags.text.toString(), - ) - } + val successfullyAddedSource = + if (existingSource != null) { + repository.updateSource( + existingSource!!.id, + binding.nameInput.text.toString(), + url, + mSpoutsValue!!, + binding.tags.text.toString(), + ) + } else { + repository.createSource( + binding.nameInput.text.toString(), + url, + mSpoutsValue!!, + binding.tags.text.toString(), + ) + } if (successfullyAddedSource) { finish() } else { Toast.makeText( this@UpsertSourceActivity, R.string.cant_create_source, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt index 11f6f62..b4a84cf 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt @@ -32,7 +32,7 @@ import org.kodein.di.instance class ItemCardAdapter( override val app: Activity, override var items: ArrayList, - override val updateItems: (ArrayList) -> Unit + override val updateItems: (ArrayList) -> Unit, ) : ItemsAdapter() { private val c: Context = app.baseContext private val imageMaxHeight: Int = @@ -42,12 +42,18 @@ class ItemCardAdapter( 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) return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + ) { with(holder) { val itm = items[position] @@ -97,7 +103,6 @@ class ItemCardAdapter( } private fun handleClickListeners() { - binding.favButton.setOnClickListener { val item = items[bindingAdapterPosition] if (item.starred) { @@ -130,7 +135,7 @@ class ItemCardAdapter( bindingAdapterPosition, items[bindingAdapterPosition].getLinkDecoded(), appSettingsService.isArticleViewerEnabled(), - app + app, ) } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt index c557b10..bdd9d6f 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt @@ -23,20 +23,26 @@ import org.kodein.di.instance class ItemListAdapter( override val app: Activity, override var items: ArrayList, - override val updateItems: (ArrayList) -> Unit + override val updateItems: (ArrayList) -> Unit, ) : ItemsAdapter() { private val c: Context = app.baseContext override val di: DI by closestDI(app) - override val repository : Repository by instance() - override val appSettingsService : AppSettingsService 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) return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + ) { with(holder) { val itm = items[position] @@ -49,7 +55,6 @@ class ItemListAdapter( binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate() if (itm.getThumbnail(repository.baseUrl).isEmpty()) { - if (itm.getIcon(repository.baseUrl).isEmpty()) { binding.itemImage.setBackgroundAndText(itm.sourcetitle.getHtmlDecoded()) } else { @@ -64,7 +69,6 @@ class ItemListAdapter( override fun getItemCount(): Int = items.size inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) { - init { handleLinkOpening() } @@ -76,7 +80,7 @@ class ItemListAdapter( bindingAdapterPosition, items[bindingAdapterPosition].getLinkDecoded(), appSettingsService.isArticleViewerEnabled(), - app + app, ) } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt index 944bd35..e1c1e9f 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt @@ -28,16 +28,20 @@ abstract class ItemsAdapter : RecyclerView.Adapte updateItems(this.items) } - private fun unmarkSnackbar(item: SelfossModel.Item, position: Int) { - val s = Snackbar - .make( - app.findViewById(R.id.coordLayout), - R.string.marked_as_read, - Snackbar.LENGTH_LONG - ) - .setAction(R.string.undo_string) { - unreadItemAtIndex(item, position, false) - } + private fun unmarkSnackbar( + item: SelfossModel.Item, + position: Int, + ) { + val s = + Snackbar + .make( + app.findViewById(R.id.coordLayout), + R.string.marked_as_read, + Snackbar.LENGTH_LONG, + ) + .setAction(R.string.undo_string) { + unreadItemAtIndex(item, position, false) + } val view = s.view val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text) @@ -45,16 +49,20 @@ abstract class ItemsAdapter : RecyclerView.Adapte s.show() } - private fun markSnackbar(item: SelfossModel.Item, position: Int) { - val s = Snackbar - .make( - app.findViewById(R.id.coordLayout), - R.string.marked_as_unread, - Snackbar.LENGTH_LONG - ) - .setAction(R.string.undo_string) { - readItemAtIndex(item, position, false) - } + private fun markSnackbar( + item: SelfossModel.Item, + position: Int, + ) { + val s = + Snackbar + .make( + app.findViewById(R.id.coordLayout), + R.string.marked_as_unread, + Snackbar.LENGTH_LONG, + ) + .setAction(R.string.undo_string) { + readItemAtIndex(item, position, false) + } val view = s.view val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text) @@ -70,7 +78,11 @@ abstract class ItemsAdapter : RecyclerView.Adapte } } - private fun readItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) { + private fun readItemAtIndex( + item: SelfossModel.Item, + position: Int, + showSnackbar: Boolean = true, + ) { CoroutineScope(Dispatchers.IO).launch { repository.markAsRead(item) } @@ -86,10 +98,13 @@ abstract class ItemsAdapter : RecyclerView.Adapte } } - private fun unreadItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) { + private fun unreadItemAtIndex( + item: SelfossModel.Item, + position: Int, + showSnackbar: Boolean = true, + ) { CoroutineScope(Dispatchers.IO).launch { repository.unmarkAsRead(item) - } notifyItemChanged(position) if (showSnackbar) { @@ -97,11 +112,13 @@ abstract class ItemsAdapter : RecyclerView.Adapte } } - fun addItemAtIndex(item: SelfossModel.Item, position: Int) { + fun addItemAtIndex( + item: SelfossModel.Item, + position: Int, + ) { items.add(position, item) notifyItemInserted(position) updateItems(items) - } fun addItemsAtEnd(newItems: List) { @@ -109,6 +126,5 @@ abstract class ItemsAdapter : RecyclerView.Adapte items.addAll(newItems) notifyItemRangeInserted(oldSize, newItems.size) updateItems(items) - } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt index 731ec51..e6422ee 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt @@ -28,20 +28,26 @@ import org.kodein.di.instance class SourcesListAdapter( private val app: Activity, - private val items: ArrayList + private val items: ArrayList, ) : RecyclerView.Adapter(), DIAware { private val c: Context = app.baseContext private lateinit var binding: SourceListItemBinding override val di: DI by closestDI(app) - private val repository : Repository by instance() + private val repository: Repository by instance() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ViewHolder { binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ViewHolder(binding.root) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + ) { val itm = items[position] if (itm.getIcon(repository.baseUrl).isEmpty()) { @@ -67,13 +73,11 @@ class SourcesListAdapter( override fun getItemCount(): Int = items.size inner class ViewHolder(private val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) { - init { handleClickListeners() } private fun handleClickListeners() { - val deleteBtn: Button = mView.findViewById(R.id.deleteBtn) deleteBtn.setOnClickListener { @@ -88,7 +92,7 @@ class SourcesListAdapter( Toast.makeText( app, R.string.can_delete_source, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } @@ -99,7 +103,6 @@ class SourcesListAdapter( repository.setSelectedSource(source) app.startActivity(Intent(app, UpsertSourceActivity::class.java)) - } } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt index 32b2ac9..8e0bc89 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt @@ -26,16 +26,15 @@ import org.kodein.di.instance import java.util.* import kotlin.concurrent.schedule -class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params), +class LoadingWorker(val context: Context, params: WorkerParameters) : + Worker(context, params), DIAware { - override val di by lazy { (applicationContext as MyApp).di } private val repository: Repository by instance() private val appSettingsService: AppSettingsService by instance() override fun doWork(): Result { if (appSettingsService.isPeriodicRefreshEnabled() && isNetworkAccessible(context)) { - CoroutineScope(Dispatchers.IO).launch { val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -67,37 +66,37 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con private fun handleNewItemsNotification( newItems: List?, - notificationManager: NotificationManager + notificationManager: NotificationManager, ) { CoroutineScope(Dispatchers.IO).launch { val apiItems = newItems.orEmpty() - val newSize = apiItems.filter { it.unread }.size if (newSize > 0) { - - val intent = Intent(context, MainActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - } - val pflags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.FLAG_IMMUTABLE - } else { - 0 - } + val intent = + Intent(context, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + val pflags = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.FLAG_IMMUTABLE + } else { + 0 + } val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, pflags) val newItemsNotification = NotificationCompat.Builder( applicationContext, - AppSettingsService.newItemsChannelId + AppSettingsService.newItemsChannelId, ) .setContentTitle(context.getString(R.string.new_items_notification_title)) .setContentText( context.getString( R.string.new_items_notification_text, - newSize - ) + newSize, + ), ) .setPriority(PRIORITY_DEFAULT) .setChannelId(AppSettingsService.newItemsChannelId) @@ -114,4 +113,4 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con } } } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt index 834f32c..6686134 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt @@ -57,7 +57,6 @@ import java.net.URL import java.util.* import java.util.concurrent.ExecutionException - private const val IMAGE_JPG = "image/jpg" class ArticleFragment : Fragment(), DIAware { @@ -84,7 +83,6 @@ class ArticleFragment : Fragment(), DIAware { private val mercuryApi: MercuryApi by instance() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -96,7 +94,7 @@ class ArticleFragment : Fragment(), DIAware { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { try { binding = FragmentArticleBinding.inflate(inflater, container, false) @@ -146,9 +144,8 @@ class ArticleFragment : Fragment(), DIAware { if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show() } } - } + }, ) - } catch (e: InflateException) { e.sendSilentlyWithAcraWithName("webview not available") if (context != null) { @@ -156,7 +153,7 @@ class ArticleFragment : Fragment(), DIAware { .setMessage(requireContext().getString(R.string.webview_dialog_issue_message)) .setTitle(requireContext().getString(R.string.webview_dialog_issue_title)) .setPositiveButton( - android.R.string.ok + android.R.string.ok, ) { _, _ -> appSettingsService.disableArticleViewer() requireActivity().finish() @@ -211,29 +208,30 @@ class ArticleFragment : Fragment(), DIAware { when (item.itemId) { R.id.share_action -> requireActivity().shareLink(url, contentTitle) R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item) - R.id.unread_action -> if (context != null) { - if (this@ArticleFragment.item.unread) { - CoroutineScope(Dispatchers.IO).launch { - repository.markAsRead(this@ArticleFragment.item) + R.id.unread_action -> + if (context != null) { + if (this@ArticleFragment.item.unread) { + CoroutineScope(Dispatchers.IO).launch { + repository.markAsRead(this@ArticleFragment.item) + } + this@ArticleFragment.item.unread = false + Toast.makeText( + context, + R.string.marked_as_read, + Toast.LENGTH_LONG, + ).show() + } else { + CoroutineScope(Dispatchers.IO).launch { + repository.unmarkAsRead(this@ArticleFragment.item) + } + this@ArticleFragment.item.unread = true + Toast.makeText( + context, + R.string.marked_as_unread, + Toast.LENGTH_LONG, + ).show() } - this@ArticleFragment.item.unread = false - Toast.makeText( - context, - R.string.marked_as_read, - Toast.LENGTH_LONG - ).show() - } else { - CoroutineScope(Dispatchers.IO).launch { - repository.unmarkAsRead(this@ArticleFragment.item) - } - this@ArticleFragment.item.unread = true - Toast.makeText( - context, - R.string.marked_as_unread, - Toast.LENGTH_LONG - ).show() } - } else -> Unit } } @@ -241,17 +239,18 @@ class ArticleFragment : Fragment(), DIAware { override fun onItemLongClick(item: MenuItem?) { // We do nothing } - } + }, ) return floatingToolbar } private fun refreshAlignment() { - textAlignment = when (appSettingsService.getActiveAllignment()) { - 1 -> "justify" - 2 -> "left" - else -> "justify" - } + textAlignment = + when (appSettingsService.getActiveAllignment()) { + 1 -> "justify" + 2 -> "left" + else -> "justify" + } } private fun getContentFromMercury() { @@ -302,7 +301,7 @@ class ArticleFragment : Fragment(), DIAware { .with(requireContext()) .asBitmap() .load( - lead_image_url + lead_image_url, ) .apply(RequestOptions.fitCenterTransform()) .into(binding.imageView) @@ -312,67 +311,75 @@ class ArticleFragment : Fragment(), DIAware { } private fun handleImageLoading() { - binding.webcontent.webViewClient = object : WebViewClient() { - @Deprecated("Deprecated in Java") - override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean { - return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { - try { - requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) - } catch (e: ActivityNotFoundException) { - e.sendSilentlyWithAcraWithName("activityNotFound > $url") - } - true - } else { - false - } - } - - @Deprecated("Deprecated in Java") - override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? { - val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL) - if (url.lowercase(Locale.US).contains(".jpg") || url.lowercase(Locale.US) - .contains(".jpeg") - ) { - try { - val image = - Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() - return WebResourceResponse( - IMAGE_JPG, - "UTF-8", - getBitmapInputStream(image, Bitmap.CompressFormat.JPEG) - ) - } catch (e: ExecutionException) { - // Do nothing - } - } else if (url.lowercase(Locale.US).contains(".png")) { - try { - val image = - Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() - return WebResourceResponse( - IMAGE_JPG, - "UTF-8", - getBitmapInputStream(image, Bitmap.CompressFormat.PNG) - ) - } catch (e: ExecutionException) { - // Do nothing - } - } else if (url.lowercase(Locale.US).contains(".webp")) { - try { - val image = - Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() - return WebResourceResponse( - IMAGE_JPG, - "UTF-8", - getBitmapInputStream(image, Bitmap.CompressFormat.WEBP) - ) - } catch (e: ExecutionException) { - // Do nothing + binding.webcontent.webViewClient = + object : WebViewClient() { + @Deprecated("Deprecated in Java") + override fun shouldOverrideUrlLoading( + view: WebView?, + url: String, + ): Boolean { + return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { + try { + requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) + } catch (e: ActivityNotFoundException) { + e.sendSilentlyWithAcraWithName("activityNotFound > $url") + } + true + } else { + false } } - return super.shouldInterceptRequest(view, url) + @Deprecated("Deprecated in Java") + override fun shouldInterceptRequest( + view: WebView, + url: String, + ): WebResourceResponse? { + val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL) + if (url.lowercase(Locale.US).contains(".jpg") || + url.lowercase(Locale.US) + .contains(".jpeg") + ) { + try { + val image = + Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() + return WebResourceResponse( + IMAGE_JPG, + "UTF-8", + getBitmapInputStream(image, Bitmap.CompressFormat.JPEG), + ) + } catch (e: ExecutionException) { + // Do nothing + } + } else if (url.lowercase(Locale.US).contains(".png")) { + try { + val image = + Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() + return WebResourceResponse( + IMAGE_JPG, + "UTF-8", + getBitmapInputStream(image, Bitmap.CompressFormat.PNG), + ) + } catch (e: ExecutionException) { + // Do nothing + } + } else if (url.lowercase(Locale.US).contains(".webp")) { + try { + val image = + Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() + return WebResourceResponse( + IMAGE_JPG, + "UTF-8", + getBitmapInputStream(image, Bitmap.CompressFormat.WEBP), + ) + } catch (e: ExecutionException) { + // Do nothing + } + } + + return super.shouldInterceptRequest(view, url) + } } - } } private fun htmlToWebview() { @@ -380,7 +387,6 @@ class ArticleFragment : Fragment(), DIAware { val attrs: IntArray = intArrayOf(android.R.attr.fontFamily) val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs) - binding.webcontent.settings.standardFontFamily = a.getString(0) binding.webcontent.visibility = View.VISIBLE @@ -397,11 +403,14 @@ class ArticleFragment : Fragment(), DIAware { handleImageLoading() val gestureDetector = - GestureDetector(activity, object : GestureDetector.SimpleOnGestureListener() { - override fun onSingleTapUp(e: MotionEvent): Boolean { - return performClick() - } - }) + GestureDetector( + activity, + object : GestureDetector.SimpleOnGestureListener() { + override fun onSingleTapUp(e: MotionEvent): Boolean { + return performClick() + } + }, + ) binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) } @@ -417,29 +426,31 @@ class ArticleFragment : Fragment(), DIAware { e.sendSilentlyWithAcraWithName("htmlToWebview > $url") } - val fontName = when (font) { - getString(R.string.open_sans_font_id) -> "Open Sans" - getString(R.string.roboto_font_id) -> "Roboto" - getString(R.string.source_code_pro_font_id) -> "Source Code Pro" - else -> "" - } + val fontName = + when (font) { + getString(R.string.open_sans_font_id) -> "Open Sans" + getString(R.string.roboto_font_id) -> "Roboto" + getString(R.string.source_code_pro_font_id) -> "Source Code Pro" + else -> "" + } - val fontLinkAndStyle = if (font.isNotEmpty()) { - """ + val fontLinkAndStyle = + if (font.isNotEmpty()) { + """ | - """.trimMargin() - } else { - "" - } + """.trimMargin() + } else { + "" + } binding.webcontent.loadDataWithBaseURL( baseUrl, @@ -457,7 +468,7 @@ class ArticleFragment : Fragment(), DIAware { | color: ${ String.format( "#%06X", - 0xFFFFFF and resources.getColor(R.color.colorAccent) + 0xFFFFFF and resources.getColor(R.color.colorAccent), ) } !important; | } @@ -473,7 +484,7 @@ class ArticleFragment : Fragment(), DIAware { | background-color: ${ String.format( "#%06X", - 0xFFFFFF and colorSurface.data + 0xFFFFFF and colorSurface.data, ) }; | } @@ -481,13 +492,13 @@ class ArticleFragment : Fragment(), DIAware { | background-color: ${ String.format( "#%06X", - 0xFFFFFF and colorSurface.data + 0xFFFFFF and colorSurface.data, ) } !important; | border-color: ${ String.format( "#%06X", - 0xFFFFFF and colorSurface.data + 0xFFFFFF and colorSurface.data, ) } !important; | padding: 0 !important; @@ -502,7 +513,7 @@ class ArticleFragment : Fragment(), DIAware { | background-color: ${ String.format( "#%06X", - 0xFFFFFF and colorSurface.data + 0xFFFFFF and colorSurface.data, ) }; | } @@ -511,10 +522,11 @@ class ArticleFragment : Fragment(), DIAware { | | | $contentText - |""".trimMargin(), + | + """.trimMargin(), "text/html", "utf-8", - null + null, ) } } @@ -535,16 +547,13 @@ class ArticleFragment : Fragment(), DIAware { requireContext().openInBrowserAsNewTask(this@ArticleFragment.item) } else { Exception("openInBrowserAfterFailing context is null").sendSilentlyWithAcraWithName("openInBrowserAfterFailing > $context") - } } companion object { private const val ARG_ITEMS = "items" - fun newInstance( - item: SelfossModel.Item - ): ArticleFragment { + fun newInstance(item: SelfossModel.Item): ArticleFragment { val fragment = ArticleFragment() val args = Bundle() args.putParcelable(ARG_ITEMS, item.toParcelable()) @@ -554,10 +563,11 @@ class ArticleFragment : Fragment(), DIAware { } fun performClick(): Boolean { - if (allImages != null && (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE || - binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) + if (allImages != null && ( + binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE || + binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE + ) ) { - val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra) val intent = Intent(activity, ImageActivity::class.java) @@ -568,6 +578,4 @@ class ArticleFragment : Fragment(), DIAware { } return false } - - } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt index d065932..5b56504 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt @@ -32,9 +32,7 @@ import org.kodein.di.DIAware import org.kodein.di.android.x.closestDI import org.kodein.di.instance - class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { - private lateinit var binding: FilterFragmentBinding override val di: DI by closestDI() private val repository: Repository by instance() @@ -44,18 +42,17 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { binding = FilterFragmentBinding.inflate( inflater, container, - false + false, ) val context: Context? = context - if (context == null) { dismiss() Exception("FilterSheetFragment context is null").sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView") @@ -77,9 +74,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { return binding.root } - private suspend fun handleSourceChips( - context: Context - ) { + private suspend fun handleSourceChips(context: Context) { val sourceGroup = binding.sourcesGroup repository.getSourcesDetailsOrStats().forEachIndexed { _, source -> @@ -88,19 +83,20 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { Glide.with(context) .load(source.getIcon(repository.baseUrl)) - .into(object : ViewTarget(c) { - override fun onResourceReady( - resource: Drawable, - transition: Transition? - ) { - try { - c.chipIcon = resource - } catch (e: Exception) { - e.sendSilentlyWithAcraWithName("sources > onResourceReady") + .into( + object : ViewTarget(c) { + override fun onResourceReady( + resource: Drawable, + transition: Transition?, + ) { + try { + c.chipIcon = resource + } catch (e: Exception) { + e.sendSilentlyWithAcraWithName("sources > onResourceReady") + } } - } - - }) + }, + ) c.text = source.title.getHtmlDecoded() @@ -121,7 +117,6 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { repository.setTagFilter(null) } - if (repository.sourceFilter.value?.equals(source) == true) { c.isCloseIconVisible = true selectedChip = c @@ -137,9 +132,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { } } - private suspend fun handleTagChips( - context: Context, - ) { + private suspend fun handleTagChips(context: Context) { val tagGroup = binding.tagsGroup val tags = repository.getTags() @@ -152,12 +145,13 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { if (tag.color.isNotEmpty()) { try { val gd = GradientDrawable() - val gdColor = try { - Color.parseColor(tag.color) - } catch (e: IllegalArgumentException) { - e.sendSilentlyWithAcraWithName("color issue " + tag.color) - resources.getColor(R.color.colorPrimary) - } + val gdColor = + try { + Color.parseColor(tag.color) + } catch (e: IllegalArgumentException) { + e.sendSilentlyWithAcraWithName("color issue " + tag.color) + resources.getColor(R.color.colorPrimary) + } gd.setColor(gdColor) gd.shape = GradientDrawable.RECTANGLE gd.setSize(30, 30) @@ -197,6 +191,4 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { companion object { const val TAG = "FilterModalBottomSheet" } - - -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ImageFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ImageFragment.kt index 7b46b8c..b9c68e0 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ImageFragment.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ImageFragment.kt @@ -11,8 +11,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.RequestOptions class ImageFragment : Fragment() { - - private lateinit var imageUrl : String + private lateinit var imageUrl: String private val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL) private var _binding: FragmentImageBinding? = null private val binding get() = _binding @@ -23,16 +22,20 @@ class ImageFragment : Fragment() { imageUrl = requireArguments().getString("imageUrl")!! } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { _binding = FragmentImageBinding.inflate(inflater, container, false) val view = binding?.root binding!!.photoView.visibility = View.VISIBLE Glide.with(requireActivity()) - .asBitmap() - .apply(glideOptions) - .load(imageUrl) - .into(binding!!.photoView) + .asBitmap() + .apply(glideOptions) + .load(imageUrl) + .into(binding!!.photoView) return view } @@ -45,9 +48,7 @@ class ImageFragment : Fragment() { companion object { private const val ARG_IMAGE = "imageUrl" - fun newInstance( - imageUrl : String - ): ImageFragment { + fun newInstance(imageUrl: String): ImageFragment { val fragment = ImageFragment() val args = Bundle() args.putString(ARG_IMAGE, imageUrl) @@ -55,4 +56,4 @@ class ImageFragment : Fragment() { return fragment } } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt index 47c7718..7db8688 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt @@ -9,21 +9,20 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.RequestOptions -fun SelfossModel.Item.preloadImages(context: Context) : Boolean { +fun SelfossModel.Item.preloadImages(context: Context): Boolean { val imageUrls = this.getImages() val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(10000) - try { for (url in imageUrls) { - if ( URLUtil.isValidUrl(url)) { + if (URLUtil.isValidUrl(url)) { Glide.with(context).asBitmap() .apply(glideOptions) .load(url).submit() } } - } catch (e : Error) { + } catch (e: Error) { e.sendSilentlyWithAcraWithName("preloadImages") return false } @@ -41,4 +40,4 @@ fun String.toTextDrawableString(): String { } } return textDrawable.toString() -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt index 7ae2939..b9ef0e4 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt @@ -4,7 +4,7 @@ import android.os.Parcel import android.os.Parcelable import bou.amine.apps.readerforselfossv2.model.SelfossModel -fun SelfossModel.Item.toParcelable() : ParecelableItem = +fun SelfossModel.Item.toParcelable(): ParecelableItem = ParecelableItem( this.id, this.datetime, @@ -17,9 +17,10 @@ fun SelfossModel.Item.toParcelable() : ParecelableItem = this.link, this.sourcetitle, this.tags.joinToString(","), - this.author + this.author, ) -fun ParecelableItem.toModel() : SelfossModel.Item = + +fun ParecelableItem.toModel(): SelfossModel.Item = SelfossModel.Item( this.id, this.datetime, @@ -32,8 +33,9 @@ fun ParecelableItem.toModel() : SelfossModel.Item = this.link, this.sourcetitle, this.tags.split(","), - this.author + this.author, ) + data class ParecelableItem( val id: Int, val datetime: String, @@ -46,15 +48,16 @@ data class ParecelableItem( val link: String, val sourcetitle: String, val tags: String, - val author: String? + val author: String?, ) : Parcelable { - companion object { @JvmField - val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel): ParecelableItem = ParecelableItem(source) - override fun newArray(size: Int): Array = arrayOfNulls(size) - } + val CREATOR: Parcelable.Creator = + object : Parcelable.Creator { + override fun createFromParcel(source: Parcel): ParecelableItem = ParecelableItem(source) + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } } constructor(source: Parcel) : this( @@ -69,12 +72,15 @@ data class ParecelableItem( link = source.readString().orEmpty(), sourcetitle = source.readString().orEmpty(), tags = source.readString().orEmpty(), - author = source.readString().orEmpty() + author = source.readString().orEmpty(), ) override fun describeContents() = 0 - override fun writeToParcel(dest: Parcel, flags: Int) { + override fun writeToParcel( + dest: Parcel, + flags: Int, + ) { dest.writeInt(id) dest.writeString(datetime) dest.writeString(title) @@ -88,4 +94,4 @@ data class ParecelableItem( dest.writeString(tags) dest.writeString(author) } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/settings/SettingsActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/settings/SettingsActivity.kt index 8d79b83..4f11ab0 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/settings/SettingsActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/settings/SettingsActivity.kt @@ -24,8 +24,10 @@ import org.kodein.di.android.closestDI private const val TITLE_TAG = "settingsActivityTitle" -class SettingsActivity : AppCompatActivity(), - PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, DIAware { +class SettingsActivity : + AppCompatActivity(), + PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, + DIAware { override val di by closestDI() override fun onCreate(savedInstanceState: Bundle?) { @@ -35,9 +37,9 @@ class SettingsActivity : AppCompatActivity(), setContentView(binding.root) if (savedInstanceState == null) { supportFragmentManager - .beginTransaction() - .replace(R.id.settings, MainPreferenceFragment()) - .commit() + .beginTransaction() + .replace(R.id.settings, MainPreferenceFragment()) + .commit() } else { title = savedInstanceState.getCharSequence(TITLE_TAG) } @@ -71,57 +73,67 @@ class SettingsActivity : AppCompatActivity(), } override fun onPreferenceStartFragment( - caller: PreferenceFragmentCompat, - pref: Preference + caller: PreferenceFragmentCompat, + pref: Preference, ): Boolean { // Instantiate the new Fragment val args = pref.extras - val fragment = supportFragmentManager.fragmentFactory.instantiate( + val fragment = + supportFragmentManager.fragmentFactory.instantiate( classLoader, - pref.fragment.toString() - ).apply { - arguments = args - setTargetFragment(caller, 0) - } + pref.fragment.toString(), + ).apply { + arguments = args + setTargetFragment(caller, 0) + } // Replace the existing Fragment with the new Fragment supportFragmentManager.beginTransaction() - .replace(R.id.settings, fragment) - .addToBackStack(null) - .commit() + .replace(R.id.settings, fragment) + .addToBackStack(null) + .commit() title = pref.title supportActionBar?.title = title return true } class MainPreferenceFragment : PreferenceFragmentCompat() { - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.pref_main, rootKey) - preferenceManager.findPreference("currentMode")?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> - AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ - true - } - - preferenceManager.findPreference("action_about")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ -> - context?.let { - LibsBuilder() - .withAboutIconShown(true) - .withAboutVersionShown(true) - .start(it) + preferenceManager.findPreference("currentMode")?.onPreferenceChangeListener = + Preference.OnPreferenceChangeListener { _, newValue -> + AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ + true + } + + preferenceManager.findPreference("action_about")?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { _ -> + context?.let { + LibsBuilder() + .withAboutIconShown(true) + .withAboutVersionShown(true) + .start(it) + } + true } - true - } } } class GeneralPreferenceFragment : PreferenceFragmentCompat() { - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.pref_general, rootKey) val editTextPreference = preferenceManager.findPreference("prefer_api_items_number") editTextPreference?.setOnBindEditTextListener { editText -> editText.inputType = InputType.TYPE_CLASS_NUMBER - editText.filters = arrayOf( + editText.filters = + arrayOf( InputFilter { source, _, _, dest, _, _ -> try { val input: Int = (dest.toString() + source.toString()).toInt() @@ -131,35 +143,53 @@ class SettingsActivity : AppCompatActivity(), Toast.makeText(activity, R.string.items_number_should_be_number, Toast.LENGTH_LONG).show() } "" - } - ) + }, + ) } } } class ArticleViewerPreferenceFragment : PreferenceFragmentCompat() { - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.pref_viewer, rootKey) val fontSize = preferenceManager.findPreference("reader_font_size") fontSize?.setOnBindEditTextListener { editText -> editText.inputType = InputType.TYPE_CLASS_NUMBER - editText.addTextChangedListener { object : TextWatcher { - override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { - // We do nothing - } - override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { - // We do nothing - } - override fun afterTextChanged(editable: Editable) { - try { - editText.textSize = editable.toString().toInt().toFloat() - } catch (e: NumberFormatException) { - e.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > afterTextChanged") + editText.addTextChangedListener { + object : TextWatcher { + override fun beforeTextChanged( + charSequence: CharSequence, + i: Int, + i1: Int, + i2: Int, + ) { + // We do nothing + } + + override fun onTextChanged( + charSequence: CharSequence, + i: Int, + i1: Int, + i2: Int, + ) { + // We do nothing + } + + override fun afterTextChanged(editable: Editable) { + try { + editText.textSize = editable.toString().toInt().toFloat() + } catch (e: NumberFormatException) { + e.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > afterTextChanged") + } } } - } } - editText.filters = arrayOf( + } + editText.filters = + arrayOf( InputFilter { source, _, _, dest, _, _ -> try { val input = (dest.toString() + source.toString()).toInt() @@ -168,26 +198,33 @@ class SettingsActivity : AppCompatActivity(), nfe.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > filters") } "" - } - ) + }, + ) } } } class OfflinePreferenceFragment : PreferenceFragmentCompat() { - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.pref_offline, rootKey) } } class ThemePreferenceFragment : PreferenceFragmentCompat() { - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.pref_theme, rootKey) - preferenceManager.findPreference("currentMode")?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> - AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ - true - } + preferenceManager.findPreference("currentMode")?.onPreferenceChangeListener = + Preference.OnPreferenceChangeListener { _, newValue -> + AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ + true + } } } @@ -197,29 +234,38 @@ class SettingsActivity : AppCompatActivity(), startActivity(browserIntent) } - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.pref_links, rootKey) - preferenceManager.findPreference("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { - openUrl(Uri.parse(AppSettingsService.trackerUrl)) - true - } + preferenceManager.findPreference("trackerLink")?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + openUrl(Uri.parse(AppSettingsService.trackerUrl)) + true + } - preferenceManager.findPreference("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { - openUrl(Uri.parse(AppSettingsService.sourceUrl)) - false - } + preferenceManager.findPreference("sourceLink")?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + openUrl(Uri.parse(AppSettingsService.sourceUrl)) + false + } - preferenceManager.findPreference("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { - openUrl(Uri.parse(AppSettingsService.translationUrl)) - false - } + preferenceManager.findPreference("translation")?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + openUrl(Uri.parse(AppSettingsService.translationUrl)) + false + } } } class ExperimentalPreferenceFragment : PreferenceFragmentCompat() { - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.pref_experimental, rootKey) } } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/AppUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/AppUtils.kt index 6fd2818..f67830d 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/AppUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/AppUtils.kt @@ -5,7 +5,10 @@ import android.content.Intent import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp -fun Context.shareLink(itemUrl: String, itemTitle: String) { +fun Context.shareLink( + itemUrl: String, + itemTitle: String, +) { val sendIntent = Intent() sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK sendIntent.action = Intent.ACTION_SEND @@ -15,7 +18,7 @@ fun Context.shareLink(itemUrl: String, itemTitle: String) { startActivity( Intent.createChooser( sendIntent, - getString(R.string.share) - ).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + getString(R.string.share), + ).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), ) -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/CircleImageView.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/CircleImageView.kt index afe81ce..31ff749 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/CircleImageView.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/CircleImageView.kt @@ -12,51 +12,54 @@ import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString import com.google.android.material.imageview.ShapeableImageView import kotlin.math.abs -class CircleImageView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : RelativeLayout(context, attrs, defStyleAttr) { - val view: View - val imageView: ShapeableImageView - val textView: TextView +class CircleImageView + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : RelativeLayout(context, attrs, defStyleAttr) { + val view: View + val imageView: ShapeableImageView + val textView: TextView - private val colorScheme = listOf( - -0x1a8c8d, - -0xf9d6e, - -0x459738, - -0x6a8a33, - -0x867935, - -0x9b4a0a, - -0xb03c09, - -0xb22f1f, - -0xb24954, - -0x7e387c, - -0x512a7f, - -0x759b, - -0x2b1ea9, - -0x2ab1, - -0x48b3, - -0x5e7781, - -0x6f5b52 - ) + private val colorScheme = + listOf( + -0x1a8c8d, + -0xf9d6e, + -0x459738, + -0x6a8a33, + -0x867935, + -0x9b4a0a, + -0xb03c09, + -0xb22f1f, + -0xb24954, + -0x7e387c, + -0x512a7f, + -0x759b, + -0x2b1ea9, + -0x2ab1, + -0x48b3, + -0x5e7781, + -0x6f5b52, + ) - init { - view = LayoutInflater.from(context).inflate(R.layout.circle_image_view, this, true) - imageView = view.findViewById(R.id.circleImage) - textView = view.findViewById(R.id.circleText) + init { + view = LayoutInflater.from(context).inflate(R.layout.circle_image_view, this, true) + imageView = view.findViewById(R.id.circleImage) + textView = view.findViewById(R.id.circleText) + } + + fun setBackgroundAndText(text: String) { + val circleDrawable = GradientDrawable() + val color = colorFromIdentifier(text) + circleDrawable.setColor(color) + imageView.setImageDrawable(circleDrawable) + + textView.text = text.toTextDrawableString() + } + + private fun colorFromIdentifier(key: String): Int { + return colorScheme[abs(key.hashCode()) % colorScheme.size] + } } - - fun setBackgroundAndText(text: String) { - val circleDrawable = GradientDrawable() - val color = colorFromIdentifier(text) - circleDrawable.setColor(color) - imageView.setImageDrawable(circleDrawable) - - textView.text = text.toTextDrawableString() - } - - private fun colorFromIdentifier(key: String): Int { - return colorScheme[abs(key.hashCode()) % colorScheme.size] - } -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt index b5db28e..a5e6d2a 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt @@ -21,14 +21,13 @@ fun Context.openItemUrl( currentItem: Int, linkDecoded: String, articleViewer: Boolean, - app: Activity + app: Activity, ) { - if (!linkDecoded.isUrlValid()) { Toast.makeText( this, this.getString(R.string.cant_open_invalid_url), - Toast.LENGTH_LONG + Toast.LENGTH_LONG, ).show() } else { if (articleViewer) { @@ -44,8 +43,7 @@ fun Context.openItemUrl( } } -fun String.isUrlValid(): Boolean = - this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches() +fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches() fun String.isBaseUrlInvalid(): Boolean { val baseUrl = this.toHttpUrlOrNull() @@ -66,7 +64,10 @@ fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) { } class LinkOnTouchListener : View.OnTouchListener { - override fun onTouch(v: View?, event: MotionEvent?): Boolean { + override fun onTouch( + v: View?, + event: MotionEvent?, + ): Boolean { var ret = false val widget: TextView = v as TextView val text: CharSequence = widget.text diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/bottombar/BottomBarUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/bottombar/BottomBarUtils.kt index 155a35c..2d93d22 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/bottombar/BottomBarUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/bottombar/BottomBarUtils.kt @@ -8,5 +8,4 @@ fun TextBadgeItem.removeBadge(): TextBadgeItem { return this } -fun TextBadgeItem.maybeShow(): TextBadgeItem = - if (this.isHidden) this.show() else this +fun TextBadgeItem.maybeShow(): TextBadgeItem = if (this.isHidden) this.show() else this diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/glide/GlideUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/glide/GlideUtils.kt index ead2bec..67b8906 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/glide/GlideUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/glide/GlideUtils.kt @@ -10,24 +10,32 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream -fun Context.bitmapCenterCrop(url: String, iv: ImageView) = - Glide.with(this) - .asBitmap() - .load(url) - .apply(RequestOptions.centerCropTransform()) - .into(iv) +fun Context.bitmapCenterCrop( + url: String, + iv: ImageView, +) = Glide.with(this) + .asBitmap() + .load(url) + .apply(RequestOptions.centerCropTransform()) + .into(iv) -fun Context.circularDrawable(url: String, view: CircleImageView) { - view.textView.text ="" +fun Context.circularDrawable( + url: String, + view: CircleImageView, +) { + view.textView.text = "" Glide.with(this) .load(url) .into(view.imageView) } -fun getBitmapInputStream(bitmap:Bitmap,compressFormat: Bitmap.CompressFormat): InputStream { +fun getBitmapInputStream( + bitmap: Bitmap, + compressFormat: Bitmap.CompressFormat, +): InputStream { val byteArrayOutputStream = ByteArrayOutputStream() bitmap.compress(compressFormat, 80, byteArrayOutputStream) val bitmapData: ByteArray = byteArrayOutputStream.toByteArray() return ByteArrayInputStream(bitmapData) -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/network/NetworkUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/network/NetworkUtils.kt index 3ed5c53..ed5f788 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/network/NetworkUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/network/NetworkUtils.kt @@ -26,4 +26,4 @@ fun isNetworkAccessible(context: Context): Boolean { val network = connectivityManager.activeNetworkInfo ?: return false return network.isConnectedOrConnecting } -} \ No newline at end of file +} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt index affba80..7cc0a81 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/viewmodel/AppViewModel.kt @@ -19,12 +19,13 @@ class AppViewModel(private val repository: Repository) : ViewModel() { if (isConnected && !wasConnected && repository.connectionMonitored) { _networkAvailableProvider.emit(true) wasConnected = true - } else if (!isConnected && wasConnected && repository.connectionMonitored){ - _networkAvailableProvider.emit(false) - wasConnected = false - } + } else if (!isConnected && wasConnected && repository.connectionMonitored) + { + _networkAvailableProvider.emit(false) + wasConnected = false + } } } } } -} \ No newline at end of file +} diff --git a/androidApp/src/test/kotlin/DatesTest.kt b/androidApp/src/test/kotlin/DatesTest.kt index b751969..04d9450 100644 --- a/androidApp/src/test/kotlin/DatesTest.kt +++ b/androidApp/src/test/kotlin/DatesTest.kt @@ -8,11 +8,10 @@ import kotlinx.datetime.toInstant import org.junit.Test class DatesTest { - - private val newVersionDateVariant = "2022-12-24T17:00:08+00" - private val newVersionDate = "2013-04-07T13:43:00+01:00" - private val oldVersionDate = "2013-05-07 13:46:00" - private val oldVersionDateVariant = "2021-03-21 10:32:00.000000" + private val newVersionDateVariant = "2022-12-24T17:00:08+00" + private val newVersionDate = "2013-04-07T13:43:00+01:00" + private val oldVersionDate = "2013-05-07 13:46:00" + private val oldVersionDateVariant = "2021-03-21 10:32:00.000000" @Test fun new_version_date_should_be_parsed() { @@ -53,5 +52,4 @@ class DatesTest { assertEquals(expected, date) } - } diff --git a/androidApp/src/test/kotlin/RepositoryTest.kt b/androidApp/src/test/kotlin/RepositoryTest.kt index 78b6ca9..c83b4a4 100644 --- a/androidApp/src/test/kotlin/RepositoryTest.kt +++ b/androidApp/src/test/kotlin/RepositoryTest.kt @@ -42,11 +42,11 @@ class RepositoryTest { private val NUMBER_STARRED = 20 private lateinit var repository: Repository - private fun initializeRepository( - isConnectionAvailable: MutableStateFlow = MutableStateFlow( - true - ) + isConnectionAvailable: MutableStateFlow = + MutableStateFlow( + true, + ), ) { repository = Repository(api, appSettingsService, isConnectionAvailable, db) @@ -64,14 +64,16 @@ class RepositoryTest { every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false - coEvery { api.apiInformation() } returns StatusAndData( - success = true, - data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(false, true)) - ) - coEvery { api.stats() } returns StatusAndData( - success = true, - data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED) - ) + coEvery { api.apiInformation() } returns + StatusAndData( + success = true, + data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(false, true)), + ) + coEvery { api.stats() } returns + StatusAndData( + success = true, + data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED), + ) every { db.itemsQueries.deleteItemsWhereSource(any()) } returns Unit every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems() @@ -101,7 +103,7 @@ class RepositoryTest { fun get_api_4_date_with_api_1_version_stored() { every { appSettingsService.getApiVersion() } returns 1 coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = true, data = generateTestApiItem()) + StatusAndData(success = true, data = generateTestApiItem()) every { appSettingsService.updateApiVersion(any()) } returns Unit initializeRepository() @@ -116,10 +118,11 @@ class RepositoryTest { @Test fun get_public_access() { every { appSettingsService.updatePublicAccess(any()) } returns Unit - coEvery { api.apiInformation() } returns StatusAndData( - success = true, - data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, true)) - ) + coEvery { api.apiInformation() } returns + StatusAndData( + success = true, + data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, true)), + ) every { appSettingsService.getUserName() } returns "" initializeRepository() @@ -131,10 +134,11 @@ class RepositoryTest { @Test fun get_public_access_username_not_empty() { every { appSettingsService.updatePublicAccess(any()) } returns Unit - coEvery { api.apiInformation() } returns StatusAndData( - success = true, - data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, true)) - ) + coEvery { api.apiInformation() } returns + StatusAndData( + success = true, + data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, true)), + ) every { appSettingsService.getUserName() } returns "username" initializeRepository() @@ -146,10 +150,11 @@ class RepositoryTest { @Test fun get_public_access_no_auth() { every { appSettingsService.updatePublicAccess(any()) } returns Unit - coEvery { api.apiInformation() } returns StatusAndData( - success = true, - data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, false)) - ) + coEvery { api.apiInformation() } returns + StatusAndData( + success = true, + data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, false)), + ) every { appSettingsService.getUserName() } returns "" initializeRepository() @@ -161,10 +166,11 @@ class RepositoryTest { @Test fun get_public_access_disabled() { every { appSettingsService.updatePublicAccess(any()) } returns Unit - coEvery { api.apiInformation() } returns StatusAndData( - success = true, - data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(false, true)) - ) + coEvery { api.apiInformation() } returns + StatusAndData( + success = true, + data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(false, true)), + ) every { appSettingsService.getUserName() } returns "" initializeRepository() @@ -180,10 +186,10 @@ class RepositoryTest { val itemParameters = FakeItemParameters() itemParameters.datetime = "2021-04-23 11:45:32" coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData( - success = true, - data = generateTestApiItem(itemParameters) - ) + StatusAndData( + success = true, + data = generateTestApiItem(itemParameters), + ) initializeRepository() runBlocking { @@ -196,7 +202,7 @@ class RepositoryTest { @Test fun get_newer_items() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = true, data = generateTestApiItem()) + StatusAndData(success = true, data = generateTestApiItem()) initializeRepository() runBlocking { @@ -211,7 +217,7 @@ class RepositoryTest { @Test fun get_all_newer_items() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = true, data = generateTestApiItem()) + StatusAndData(success = true, data = generateTestApiItem()) initializeRepository() repository.displayedItems = ItemType.ALL @@ -227,7 +233,7 @@ class RepositoryTest { @Test fun get_newer_starred_items() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = true, data = generateTestApiItem()) + StatusAndData(success = true, data = generateTestApiItem()) initializeRepository() repository.displayedItems = ItemType.STARRED @@ -264,10 +270,10 @@ class RepositoryTest { itemParameter3.tags = "Other, Tag" itemParameter3.id = "3" coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems( - itemParameter1 + itemParameter1, ) + - generateTestDBItems(itemParameter2) + - generateTestDBItems(itemParameter3) + generateTestDBItems(itemParameter2) + + generateTestDBItems(itemParameter3) every { appSettingsService.isItemCachingEnabled() } returns true @@ -292,24 +298,26 @@ class RepositoryTest { itemParameter3.sourcetitle = "Other" itemParameter3.id = "3" coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems( - itemParameter1 + itemParameter1, ) + - generateTestDBItems(itemParameter2) + - generateTestDBItems(itemParameter3) + generateTestDBItems(itemParameter2) + + generateTestDBItems(itemParameter3) every { appSettingsService.isItemCachingEnabled() } returns true initializeRepository(MutableStateFlow(false)) - repository.setSourceFilter(SelfossModel.SourceDetail( - 1, - "Test", - null, - listOf("tags"), - SPOUT, - "", - IMAGE_URL, - SelfossModel.SourceParams("url") - )) + repository.setSourceFilter( + SelfossModel.SourceDetail( + 1, + "Test", + null, + listOf("tags"), + SPOUT, + "", + IMAGE_URL, + SelfossModel.SourceParams("url"), + ), + ) runBlocking { repository.getNewerItems() } @@ -322,7 +330,7 @@ class RepositoryTest { @Test fun get_older_items() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = true, data = generateTestApiItem()) + StatusAndData(success = true, data = generateTestApiItem()) initializeRepository() repository.items = ArrayList(generateTestApiItem()) @@ -338,7 +346,7 @@ class RepositoryTest { @Test fun get_all_older_items() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = true, data = generateTestApiItem()) + StatusAndData(success = true, data = generateTestApiItem()) initializeRepository() repository.items = ArrayList(generateTestApiItem()) @@ -355,7 +363,7 @@ class RepositoryTest { @Test fun get_older_starred_items() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = true, data = generateTestApiItem()) + StatusAndData(success = true, data = generateTestApiItem()) initializeRepository() repository.displayedItems = ItemType.STARRED @@ -592,14 +600,16 @@ class RepositoryTest { } private fun prepareTags(): Pair, List> { - val tags = listOf( - SelfossModel.Tag("test", "red", 6), - SelfossModel.Tag("second", "yellow", 0) - ) - val tagsDB = listOf( - TAG("test_DB", "red", 6), - TAG("second_DB", "yellow", 0) - ) + val tags = + listOf( + SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0), + ) + val tagsDB = + listOf( + TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0), + ) coEvery { api.tags() } returns StatusAndData(success = true, data = tags) coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB @@ -621,48 +631,50 @@ class RepositoryTest { } private fun prepareSources(): Pair, List> { - val sources = arrayListOf( - SelfossModel.SourceDetail( - 1, - "First source", - null, - listOf("Test", "second"), - SPOUT, - "", - IMAGE_URL_2, - SelfossModel.SourceParams("url") - ), - SelfossModel.SourceDetail( - 2, - "Second source", - null, - listOf("second"), - SPOUT, - "", - IMAGE_URL, - SelfossModel.SourceParams("url") + val sources = + arrayListOf( + SelfossModel.SourceDetail( + 1, + "First source", + null, + listOf("Test", "second"), + SPOUT, + "", + IMAGE_URL_2, + SelfossModel.SourceParams("url"), + ), + SelfossModel.SourceDetail( + 2, + "Second source", + null, + listOf("second"), + SPOUT, + "", + IMAGE_URL, + SelfossModel.SourceParams("url"), + ), ) - ) - val sourcesDB = listOf( - SOURCE( - "1", - "First DB source", - "Test,second", - SPOUT, - "", - IMAGE_URL_2, - "url" - ), - SOURCE( - "2", - "Second source", - "second", - SPOUT, - "", - IMAGE_URL, - "url" + val sourcesDB = + listOf( + SOURCE( + "1", + "First DB source", + "Test,second", + SPOUT, + "", + IMAGE_URL_2, + "url", + ), + SOURCE( + "2", + "Second source", + "second", + SPOUT, + "", + IMAGE_URL, + "url", + ), ) - ) coEvery { api.sourcesDetailed() } returns StatusAndData(success = true, data = sources) every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB @@ -791,17 +803,18 @@ class RepositoryTest { @Test fun create_source() { coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns - SuccessResponse(true) + SuccessResponse(true) initializeRepository() var response: Boolean runBlocking { - response = repository.createSource( - "test", - FEED_URL, - SPOUT, - TAGS, - ) + response = + repository.createSource( + "test", + FEED_URL, + SPOUT, + TAGS, + ) } coVerify(exactly = 1) { @@ -818,17 +831,18 @@ class RepositoryTest { @Test fun create_source_but_response_fails() { coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns - SuccessResponse(false) + SuccessResponse(false) initializeRepository() var response: Boolean runBlocking { - response = repository.createSource( - "test", - FEED_URL, - SPOUT, - TAGS - ) + response = + repository.createSource( + "test", + FEED_URL, + SPOUT, + TAGS, + ) } coVerify(exactly = 1) { @@ -836,7 +850,7 @@ class RepositoryTest { any(), any(), any(), - any() + any(), ) } assertSame(false, response) @@ -845,17 +859,18 @@ class RepositoryTest { @Test fun create_source_without_connection() { coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns - SuccessResponse(true) + SuccessResponse(true) initializeRepository(MutableStateFlow(false)) var response: Boolean runBlocking { - response = repository.createSource( - "test", - FEED_URL, - SPOUT, - TAGS - ) + response = + repository.createSource( + "test", + FEED_URL, + SPOUT, + TAGS, + ) } coVerify(exactly = 0) { @@ -916,10 +931,11 @@ class RepositoryTest { @Test fun update_remote() { - coEvery { api.update() } returns StatusAndData( - success = true, - data = "finished" - ) + coEvery { api.update() } returns + StatusAndData( + success = true, + data = "finished", + ) initializeRepository() var response: Boolean @@ -933,10 +949,11 @@ class RepositoryTest { @Test fun update_remote_but_response_fails() { - coEvery { api.update() } returns StatusAndData( - success = false, - data = "unallowed access" - ) + coEvery { api.update() } returns + StatusAndData( + success = false, + data = "unallowed access", + ) initializeRepository() var response: Boolean @@ -950,10 +967,11 @@ class RepositoryTest { @Test fun update_remote_with_unallowed_access() { - coEvery { api.update() } returns StatusAndData( - success = true, - data = "unallowed access" - ) + coEvery { api.update() } returns + StatusAndData( + success = true, + data = "unallowed access", + ) initializeRepository() var response: Boolean @@ -967,10 +985,11 @@ class RepositoryTest { @Test fun update_remote_without_connection() { - coEvery { api.update() } returns StatusAndData( - success = true, - data = "undocumented..." - ) + coEvery { api.update() } returns + StatusAndData( + success = true, + data = "undocumented...", + ) initializeRepository(MutableStateFlow(false)) var response: Boolean @@ -1037,7 +1056,7 @@ class RepositoryTest { appSettingsService.refreshLoginInformation( BASE_URL, "login", - "password" + "password", ) } } @@ -1057,13 +1076,14 @@ class RepositoryTest { any(), any(), any(), - any() + any(), + ) + } returnsMany + listOf( + StatusAndData(success = true, data = generateTestApiItem(itemParameter1)), + StatusAndData(success = true, data = generateTestApiItem(itemParameter2)), + StatusAndData(success = true, data = generateTestApiItem(itemParameter1)), ) - } returnsMany listOf( - StatusAndData(success = true, data = generateTestApiItem(itemParameter1)), - StatusAndData(success = true, data = generateTestApiItem(itemParameter2)), - StatusAndData(success = true, data = generateTestApiItem(itemParameter1)), - ) initializeRepository() prepareSearch() @@ -1077,7 +1097,7 @@ class RepositoryTest { @Test fun cache_items_but_response_fails() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = false, data = generateTestApiItem()) + StatusAndData(success = false, data = generateTestApiItem()) initializeRepository() prepareSearch() @@ -1091,7 +1111,7 @@ class RepositoryTest { @Test fun cache_items_without_connection() { coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns - StatusAndData(success = false, data = generateTestApiItem()) + StatusAndData(success = false, data = generateTestApiItem()) initializeRepository(MutableStateFlow(false)) prepareSearch() @@ -1113,9 +1133,9 @@ class RepositoryTest { SPOUT, "", IMAGE_URL_2, - SelfossModel.SourceParams("url") - ) + SelfossModel.SourceParams("url"), + ), ) repository.searchFilter = "search" } -} \ No newline at end of file +} diff --git a/androidApp/src/test/kotlin/TestUtils.kt b/androidApp/src/test/kotlin/TestUtils.kt index 0578e26..642f8fe 100644 --- a/androidApp/src/test/kotlin/TestUtils.kt +++ b/androidApp/src/test/kotlin/TestUtils.kt @@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.repository import bou.amine.apps.readerforselfossv2.dao.ITEM import bou.amine.apps.readerforselfossv2.model.SelfossModel - fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List { return listOf( ITEM( @@ -18,8 +17,8 @@ fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List?, authType: String?) {} + override fun checkClientTrusted( + chain: Array?, + authType: String?, + ) {} - override fun checkServerTrusted(chain: Array?, authType: String?) {} + override fun checkServerTrusted( + chain: Array?, + authType: String?, + ) {} override fun getAcceptedIssuers(): Array = arrayOf() } actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) { config.https.trustManager = NaiveTrustManager() -} \ No newline at end of file +} diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt index 5f8ea41..d6f1127 100644 --- a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt @@ -3,40 +3,40 @@ package bou.amine.apps.readerforselfossv2.utils import android.text.format.DateUtils import kotlinx.datetime.* - actual class DateUtils { actual companion object { - // Possible formats are // yyyy-mm-dd hh:mm:ss format private val oldVersionFormat = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(.()\\d*)?".toRegex() + // yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX (RFC3339) private val newVersionFormat = "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}(:\\d{2})?".toRegex() // We may need to consider moving the formatting to platform specific code, even if the tests are doubled // For now, we handle this in a hacky way, because kotlin only accepts iso formats actual fun parseDate(dateString: String): Long { - var isoDateString: String = if (dateString.matches(oldVersionFormat)) { - dateString.replace(" ", "T") - } else if (dateString.matches(newVersionFormat)) { - dateString.split("+")[0] - } else { - throw Exception("Unrecognized format for $dateString") - } + var isoDateString: String = + if (dateString.matches(oldVersionFormat)) { + dateString.replace(" ", "T") + } else if (dateString.matches(newVersionFormat)) { + dateString.split("+")[0] + } else { + throw Exception("Unrecognized format for $dateString") + } return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() } actual fun parseRelativeDate(dateString: String): String { - val date = parseDate(dateString) - return " " + DateUtils.getRelativeTimeSpanString( - date, - Clock.System.now().toEpochMilliseconds(), - DateUtils.MINUTE_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE - ) + return " " + + DateUtils.getRelativeTimeSpanString( + date, + Clock.System.now().toEpochMilliseconds(), + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE, + ) } } -} \ No newline at end of file +} diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt index 3999b3d..e5100cf 100644 --- a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -21,13 +21,13 @@ actual fun SelfossModel.Item.getThumbnail(baseUrl: String): String { actual fun SelfossModel.Item.getImages(): ArrayList { val allImages = ArrayList() - for ( image in Jsoup.parse(content).getElementsByTag("img")) { + for (image in Jsoup.parse(content).getElementsByTag("img")) { val url = image.attr("src") if (url.lowercase(Locale.US).contains(".jpg") || url.lowercase(Locale.US).contains(".jpeg") || url.lowercase(Locale.US).contains(".png") || - url.lowercase(Locale.US).contains(".webp")) - { + url.lowercase(Locale.US).contains(".webp") + ) { allImages.add(url) } } @@ -38,7 +38,11 @@ actual fun SelfossModel.Source.getIcon(baseUrl: String): String { return constructUrl(baseUrl, "favicons", icon) } -actual fun constructUrl(baseUrl: String, path: String, file: String?): String { +actual fun constructUrl( + baseUrl: String, + path: String, + file: String?, +): String { return if (file == null || file == "null" || file.isEmpty()) { "" } else { @@ -47,4 +51,4 @@ actual fun constructUrl(baseUrl: String, path: String, file: String?): String { baseUriBuilder.toString() } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/DI/modules.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/DI/modules.kt index e7c2a2b..f099de4 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/DI/modules.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/DI/modules.kt @@ -2,7 +2,6 @@ package bou.amine.apps.readerforselfossv2.DI import bou.amine.apps.readerforselfossv2.rest.MercuryApi import bou.amine.apps.readerforselfossv2.rest.SelfossApi -import bou.amine.apps.readerforselfossv2.service.AppSettingsService import org.kodein.di.DI import org.kodein.di.bind import org.kodein.di.instance @@ -11,4 +10,4 @@ import org.kodein.di.singleton val networkModule by DI.Module { bind() with singleton { SelfossApi(instance()) } bind() with singleton { MercuryApi() } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt index 55d046f..eb28bb8 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt @@ -4,4 +4,4 @@ import com.squareup.sqldelight.db.SqlDriver expect class DriverFactory { fun createDriver(): SqlDriver -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/MercuryModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/MercuryModel.kt index d566a7b..05c7666 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/MercuryModel.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/MercuryModel.kt @@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.model import kotlinx.serialization.Serializable class MercuryModel { - @Serializable class ParsedContent( val title: String? = null, @@ -12,6 +11,6 @@ class MercuryModel { val url: String? = null, val error: Boolean? = null, val message: String? = null, - val failed: Boolean? = null + val failed: Boolean? = null, ) } diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/NetworkUnavailableException.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/NetworkUnavailableException.kt index a51fb77..f12e035 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/NetworkUnavailableException.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/NetworkUnavailableException.kt @@ -1,3 +1,3 @@ package bou.amine.apps.readerforselfossv2.model -class NetworkUnavailableException : Exception() \ No newline at end of file +class NetworkUnavailableException : Exception() diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/ResultModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/ResultModel.kt index e067389..48578b4 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/ResultModel.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/ResultModel.kt @@ -18,4 +18,4 @@ class StatusAndData(val success: Boolean, val data: T? = null) { return StatusAndData(false) } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt index 9068c9a..34fc7e9 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt @@ -13,32 +13,31 @@ import kotlinx.serialization.encoding.encodeCollection import kotlinx.serialization.json.* class SelfossModel { - @Serializable data class Tag( val tag: String, val color: String, - val unread: Int + val unread: Int, ) @Serializable class Stats( val total: Int, val unread: Int? = null, - val starred: Int? = null + val starred: Int? = null, ) @Serializable data class Spout( val name: String, - val description: String + val description: String, ) @Serializable data class ApiInformation( val version: String? = null, val apiversion: String? = null, - val configuration: ApiConfiguration? = null + val configuration: ApiConfiguration? = null, ) { fun getApiMajorVersion(): Int { var versionNumber = 0 @@ -56,7 +55,7 @@ class SelfossModel { @Serializable(with = BooleanSerializer::class) val publicMode: Boolean? = null, @Serializable(with = BooleanSerializer::class) - val authEnabled: Boolean? = null + val authEnabled: Boolean? = null, ) { fun isAuthEnabled() = authEnabled ?: true @@ -77,8 +76,8 @@ class SelfossModel { override var title: String, override var unread: Int? = null, override var error: String? = null, - override var icon: String? = null - ) : Source + override var icon: String? = null, + ) : Source @Serializable data class SourceDetail( @@ -90,13 +89,14 @@ class SelfossModel { var spout: String? = null, override var error: String? = null, override var icon: String? = null, - var params: SourceParams? = null + var params: SourceParams? = null, ) : Source @Serializable data class SourceParams( - val url: String? = null + val url: String? = null, ) + @Serializable data class Item( val id: Int, @@ -113,15 +113,16 @@ class SelfossModel { val sourcetitle: String, @Serializable(with = TagsListSerializer::class) val tags: List, - val author: String? = null + val author: String? = null, ) { fun getLinkDecoded(): String { var stringUrl: String - stringUrl = if (link.contains("//news.google.com/news/") && link.contains("&url=")) { - link.substringAfter("&url=") - } else { - this.link.replace("&", "&") - } + stringUrl = + if (link.contains("//news.google.com/news/") && link.contains("&url=")) { + link.substringAfter("&url=") + } else { + this.link.replace("&", "&") + } // handle :443 => https if (stringUrl.contains(":443")) { @@ -151,21 +152,22 @@ class SelfossModel { } } - // TODO: this seems to be super slow. object TagsListSerializer : KSerializer> { override fun deserialize(decoder: Decoder): List { - return when(val json = ((decoder as JsonDecoder).decodeJsonElement())) { + return when (val json = ((decoder as JsonDecoder).decodeJsonElement())) { is JsonArray -> json.toList().map { it.toString().replace("^\"|\"$".toRegex(), "") } else -> json.toString().split(",") } - } override val descriptor: SerialDescriptor get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING) - override fun serialize(encoder: Encoder, value: List) { + override fun serialize( + encoder: Encoder, + value: List, + ) { encoder.encodeCollection(PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING), value.size) { this.toString() } } } @@ -183,7 +185,10 @@ class SelfossModel { override val descriptor: SerialDescriptor get() = PrimitiveSerialDescriptor("b", PrimitiveKind.BOOLEAN) - override fun serialize(encoder: Encoder, value: Boolean) { + override fun serialize( + encoder: Encoder, + value: Boolean, + ) { TODO("Not yet implemented") } } diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt index 8d6cff4..8968313 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt @@ -19,9 +19,8 @@ class Repository( private val api: SelfossApi, private val appSettingsService: AppSettingsService, val isConnectionAvailable: MutableStateFlow, - private val db: ReaderForSelfossDB + private val db: ReaderForSelfossDB, ) { - var items = ArrayList() var connectionMonitored = false @@ -53,20 +52,22 @@ class Repository( suspend fun getNewerItems(): ArrayList { var fetchedItems: StatusAndData> = StatusAndData.error() if (isNetworkAvailable()) { - fetchedItems = api.getItems( - displayedItems.type, - offset = 0, - tagFilter.value?.tag, - sourceFilter.value?.id?.toLong(), - searchFilter, - null - ) + fetchedItems = + api.getItems( + displayedItems.type, + offset = 0, + tagFilter.value?.tag, + sourceFilter.value?.id?.toLong(), + searchFilter, + null, + ) } else if (appSettingsService.isItemCachingEnabled()) { - var dbItems = getDBItems().filter { - displayedItems == ItemType.ALL || + var dbItems = + getDBItems().filter { + displayedItems == ItemType.ALL || (it.unread && displayedItems == ItemType.UNREAD) || (it.starred && displayedItems == ItemType.STARRED) - } + } if (tagFilter.value != null) { dbItems = dbItems.filter { it.tags.split(',').contains(tagFilter.value!!.tag) } } @@ -75,9 +76,10 @@ class Repository( } val itemsList = ArrayList(dbItems.map { it.toView() }) itemsList.sortByDescending { DateUtils.parseDate(it.datetime) } - fetchedItems = StatusAndData.succes( - itemsList - ) + fetchedItems = + StatusAndData.succes( + itemsList, + ) } if (fetchedItems.success && fetchedItems.data != null) { @@ -90,14 +92,15 @@ class Repository( var fetchedItems: StatusAndData> = StatusAndData.error() if (isNetworkAvailable()) { val offset = items.size - fetchedItems = api.getItems( - displayedItems.type, - offset, - tagFilter.value?.tag, - sourceFilter.value?.id?.toLong(), - searchFilter, - null - ) + fetchedItems = + api.getItems( + displayedItems.type, + offset, + tagFilter.value?.tag, + sourceFilter.value?.id?.toLong(), + searchFilter, + null, + ) } // When using the db cache, we load everything the first time, so there should be nothing more to load. if (fetchedItems.success && fetchedItems.data != null) { @@ -108,15 +111,16 @@ class Repository( private suspend fun getMaxItemsForBackground(itemType: ItemType): List { return if (isNetworkAvailable()) { - val items = api.getItems( - itemType.type, - 0, - null, - null, - null, - null, - 200 - ) + val items = + api.getItems( + itemType.type, + 0, + null, + null, + null, + null, + 200, + ) return if (items.success && items.data != null) { items.data } else { @@ -374,7 +378,7 @@ class Repository( title: String, url: String, spout: String, - tags: String + tags: String, ): Boolean { var response = false if (isNetworkAvailable()) { @@ -384,7 +388,10 @@ class Repository( return response } - suspend fun deleteSource(id: Int, title: String): Boolean { + suspend fun deleteSource( + id: Int, + title: String, + ): Boolean { var success = false if (isNetworkAvailable()) { val response = api.deleteSource(id) @@ -456,7 +463,11 @@ class Repository( } } - fun refreshLoginInformation(url: String, login: String, password: String) { + fun refreshLoginInformation( + url: String, + login: String, + password: String, + ) { appSettingsService.refreshLoginInformation(url, login, password) baseUrl = url api.refreshLoginInformation() @@ -474,9 +485,10 @@ class Repository( // Check if we're accessing the instance in public mode // This happens when auth and public mode are enabled but // no credentials are provided to login - if (appSettingsService.getUserName().isEmpty() - && fetchedInformation.data.getApiConfiguration().isAuthEnabled() - && fetchedInformation.data.getApiConfiguration().isPublicModeEnabled()) { + if (appSettingsService.getUserName().isEmpty() && + fetchedInformation.data.getApiConfiguration().isAuthEnabled() && + fetchedInformation.data.getApiConfiguration().isPublicModeEnabled() + ) { appSettingsService.updatePublicAccess(true) } } @@ -485,11 +497,9 @@ class Repository( fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride - private fun getDBActions(): List = - db.actionsQueries.actions().executeAsList() + private fun getDBActions(): List = db.actionsQueries.actions().executeAsList() - private fun deleteDBAction(action: ACTION) = - db.actionsQueries.deleteAction(action.id) + private fun deleteDBAction(action: ACTION) = db.actionsQueries.deleteAction(action.id) private fun getDBTags(): List = db.tagsQueries.tags().executeAsList() @@ -530,9 +540,8 @@ class Repository( read: Boolean = false, unread: Boolean = false, starred: Boolean = false, - unstarred: Boolean = false - ) = - db.actionsQueries.insertAction(articleid, read, unread, starred, unstarred) + unstarred: Boolean = false, + ) = db.actionsQueries.insertAction(articleid, read, unread, starred, unstarred) private fun updateDBItem(item: SelfossModel.Item) = db.itemsQueries.updateItem( @@ -547,7 +556,7 @@ class Repository( item.sourcetitle, item.tags.joinToString(","), item.author, - item.id.toString() + item.id.toString(), ) suspend fun tryToCacheItemsAndGetNewOnes(): List { @@ -564,32 +573,38 @@ class Repository( } suspend fun handleDBActions() { - val actions: List = getDBActions() actions.forEach { action -> when { - action.read -> doAndReportOnFail( - markAsReadById(action.articleid.toInt()), - action - ) - action.unread -> doAndReportOnFail( - unmarkAsReadById(action.articleid.toInt()), - action - ) - action.starred -> doAndReportOnFail( - starrById(action.articleid.toInt()), - action - ) - action.unstarred -> doAndReportOnFail( - unstarrById(action.articleid.toInt()), - action - ) + action.read -> + doAndReportOnFail( + markAsReadById(action.articleid.toInt()), + action, + ) + action.unread -> + doAndReportOnFail( + unmarkAsReadById(action.articleid.toInt()), + action, + ) + action.starred -> + doAndReportOnFail( + starrById(action.articleid.toInt()), + action, + ) + action.unstarred -> + doAndReportOnFail( + unstarrById(action.articleid.toInt()), + action, + ) } } } - private fun doAndReportOnFail(result: Boolean, action: ACTION) { + private fun doAndReportOnFail( + result: Boolean, + action: ACTION, + ) { if (result) { deleteDBAction(action) } @@ -626,4 +641,4 @@ class Repository( fun getSelectedSource(): SelfossModel.SourceDetail? { return _selectedSource } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt index ab126bc..deec323 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt @@ -11,25 +11,27 @@ import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json class MercuryApi() { - var client = createHttpClient() private fun createHttpClient(): HttpClient { return HttpClient { install(ContentNegotiation) { install(HttpCache) - json(Json { - prettyPrint = true - isLenient = true - ignoreUnknownKeys = true - }) + json( + Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + }, + ) } install(Logging) { - logger = object : Logger { - override fun log(message: String) { - Napier.d(message, tag = "LogMercuryCalls") + logger = + object : Logger { + override fun log(message: String) { + Napier.d(message, tag = "LogMercuryCalls") + } } - } level = LogLevel.INFO } expectSuccess = false @@ -37,7 +39,9 @@ class MercuryApi() { } suspend fun query(url: String): StatusAndData = - bodyOrFailure(client.get("https://amine-louveau.fr/parser.php") { - parameter("link", url) - }) -} \ No newline at end of file + bodyOrFailure( + client.get("https://amine-louveau.fr/parser.php") { + parameter("link", url) + }, + ) +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/RestUtils.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/RestUtils.kt index 05b194a..be5b12e 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/RestUtils.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/RestUtils.kt @@ -10,7 +10,6 @@ import io.ktor.client.request.forms.* import io.ktor.client.statement.* import io.ktor.http.* - suspend fun responseOrSuccessIf404(r: HttpResponse?): SuccessResponse { return if (r != null && r.status === HttpStatusCode.NotFound) { SuccessResponse(true) @@ -40,7 +39,7 @@ suspend inline fun bodyOrFailure(r: HttpResponse?): StatusAndData inline fun tryToRequest( requestType: String, - fn: () -> HttpResponse + fn: () -> HttpResponse, ): HttpResponse? { var response: HttpResponse? = null try { @@ -53,30 +52,46 @@ inline fun tryToRequest( suspend inline fun HttpClient.tryToGet( urlString: String, - crossinline block: HttpRequestBuilder.() -> Unit = {} -): HttpResponse? = tryToRequest("Get") { return this.get { url(urlString); block() } } - + crossinline block: HttpRequestBuilder.() -> Unit = {}, +): HttpResponse? = + tryToRequest("Get") { + return this.get { + url(urlString) + block() + } + } suspend inline fun HttpClient.tryToPost( urlString: String, - block: HttpRequestBuilder.() -> Unit = {} -): HttpResponse? = tryToRequest("Post") { return this.post { url(urlString); block() } } + block: HttpRequestBuilder.() -> Unit = {}, +): HttpResponse? = + tryToRequest("Post") { + return this.post { + url(urlString) + block() + } + } suspend inline fun HttpClient.tryToDelete( urlString: String, - block: HttpRequestBuilder.() -> Unit = {} -): HttpResponse? = tryToRequest("Delete") { return this.delete { url(urlString); block() } } - + block: HttpRequestBuilder.() -> Unit = {}, +): HttpResponse? = + tryToRequest("Delete") { + return this.delete { + url(urlString) + block() + } + } suspend fun HttpClient.tryToSubmitForm( url: String, formParameters: Parameters = Parameters.Empty, encodeInQuery: Boolean = false, - block: HttpRequestBuilder.() -> Unit = {} + block: HttpRequestBuilder.() -> Unit = {}, ): HttpResponse? = tryToRequest("SubmitForm") { return this.submitForm(formParameters, encodeInQuery) { url(url) block() } - } \ No newline at end of file + } diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt index d010080..f0198c0 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt @@ -36,8 +36,8 @@ import kotlinx.serialization.json.Json expect fun setupInsecureHTTPEngine(config: CIOEngineConfig) class SelfossApi(private val appSettingsService: AppSettingsService) { - var client = createHttpClient() + fun createHttpClient() = HttpClient(CIO) { if (appSettingsService.getSelfSigned()) { @@ -47,19 +47,22 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { } install(ContentNegotiation) { install(HttpCache) - json(Json { - prettyPrint = true - isLenient = true - ignoreUnknownKeys = true - explicitNulls = false - }) + json( + Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + explicitNulls = false + }, + ) } install(Logging) { - logger = object : Logger { - override fun log(message: String) { - Napier.d(message, tag = "LogApiCalls") + logger = + object : Logger { + override fun log(message: String) { + Napier.d(message, tag = "LogApiCalls") + } } - } level = LogLevel.INFO } install(HttpTimeout) { @@ -83,8 +86,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { expectSuccess = false } - fun url(path: String) = - "${appSettingsService.getBaseUrl()}$path" + fun url(path: String) = "${appSettingsService.getBaseUrl()}$path" fun refreshLoginInformation() { appSettingsService.refreshApiSettings() @@ -100,12 +102,15 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { // Api version was introduces after the POST login, so when there is a version, it should be available private fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1 + private fun hasLoginInfo() = - appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword() - .isNotEmpty() + appSettingsService.getUserName().isNotEmpty() && + appSettingsService.getPassword() + .isNotEmpty() suspend fun login(): SuccessResponse = - if (appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword() + if (appSettingsService.getUserName().isNotEmpty() && + appSettingsService.getPassword() .isNotEmpty() ) { if (shouldHavePostLogin()) { @@ -117,30 +122,49 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { SuccessResponse(true) } - private suspend fun getLogin() = maybeResponse(client.tryToGet(url("/login")) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) + private suspend fun getLogin() = + maybeResponse( + client.tryToGet(url("/login")) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) - private suspend fun postLogin() = maybeResponse(client.tryToPost(url("/login")) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) + private suspend fun postLogin() = + maybeResponse( + client.tryToPost(url("/login")) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) - private fun shouldHaveNewLogout() = - appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0 + private fun shouldHaveNewLogout() = appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0 suspend fun logout(): SuccessResponse = if (shouldHaveNewLogout()) { @@ -150,23 +174,42 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { } private suspend fun maybeLogoutIfAvailable() = - responseOrSuccessIf404(client.tryToGet(url("/logout")) { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) + responseOrSuccessIf404( + client.tryToGet(url("/logout")) { + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } } - } - }) + }, + ) - private suspend fun doLogout() = maybeResponse(client.tryToDelete(url("/api/session/current")) { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) + private suspend fun doLogout() = + maybeResponse( + client.tryToDelete(url("/api/session/current")) { + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) suspend fun getItems( type: String, @@ -175,213 +218,340 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { source: Long?, search: String?, updatedSince: String?, - items: Int? = null + items: Int? = null, ): StatusAndData> = - bodyOrFailure(client.tryToGet(url("/items")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - parameter("type", type) - parameter("tag", tag) - parameter("source", source) - parameter("search", search) - parameter("updatedsince", updatedSince) - parameter("items", items ?: appSettingsService.getItemsNumber()) - parameter("offset", offset) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun getItemsWithoutCatch(): StatusAndData> = - bodyOrFailure(client.get(url("/items")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - parameter("type", "all") - parameter("items", 1) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun stats(): StatusAndData = - bodyOrFailure(client.tryToGet(url("/stats")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun tags(): StatusAndData> = - bodyOrFailure(client.tryToGet(url("/tags")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun update(): StatusAndData = - bodyOrFailure(client.tryToGet(url("/update")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun spouts(): StatusAndData> = - bodyOrFailure(client.tryToGet(url("/sources/spouts")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun sourcesStats(): StatusAndData> = - bodyOrFailure(client.tryToGet(url("/sources/stats")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun sourcesDetailed(): StatusAndData> = - bodyOrFailure(client.tryToGet(url("/sources/list")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun apiInformation(): StatusAndData = - bodyOrFailure(client.tryToGet(url("/api/about")) { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun markAsRead(id: String): SuccessResponse = - maybeResponse(client.tryToPost(url("/mark/$id")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun unmarkAsRead(id: String): SuccessResponse = - maybeResponse(client.tryToPost(url("/unmark/$id")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun starr(id: String): SuccessResponse = - maybeResponse(client.tryToPost(url("/starr/$id")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun unstarr(id: String): SuccessResponse = - maybeResponse(client.tryToPost(url("/unstarr/$id")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) - ) - } - } - }) - - suspend fun markAllAsRead(ids: List): SuccessResponse = - maybeResponse(client.tryToSubmitForm( - url = url("/mark"), - formParameters = Parameters.build { + bodyOrFailure( + client.tryToGet(url("/items")) { if (!shouldHavePostLogin()) { - append("username", appSettingsService.getUserName()) - append("password", appSettingsService.getPassword()) + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) } - ids.map { append("ids[]", it) } - }, - block = { + parameter("type", type) + parameter("tag", tag) + parameter("source", source) + parameter("search", search) + parameter("updatedsince", updatedSince) + parameter("items", items ?: appSettingsService.getItemsNumber()) + parameter("offset", offset) if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), ) } } - } - )) + }, + ) + + suspend fun getItemsWithoutCatch(): StatusAndData> = + bodyOrFailure( + client.get(url("/items")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + parameter("type", "all") + parameter("items", 1) + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun stats(): StatusAndData = + bodyOrFailure( + client.tryToGet(url("/stats")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun tags(): StatusAndData> = + bodyOrFailure( + client.tryToGet(url("/tags")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun update(): StatusAndData = + bodyOrFailure( + client.tryToGet(url("/update")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun spouts(): StatusAndData> = + bodyOrFailure( + client.tryToGet(url("/sources/spouts")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun sourcesStats(): StatusAndData> = + bodyOrFailure( + client.tryToGet(url("/sources/stats")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun sourcesDetailed(): StatusAndData> = + bodyOrFailure( + client.tryToGet(url("/sources/list")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun apiInformation(): StatusAndData = + bodyOrFailure( + client.tryToGet(url("/api/about")) { + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun markAsRead(id: String): SuccessResponse = + maybeResponse( + client.tryToPost(url("/mark/$id")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun unmarkAsRead(id: String): SuccessResponse = + maybeResponse( + client.tryToPost(url("/unmark/$id")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun starr(id: String): SuccessResponse = + maybeResponse( + client.tryToPost(url("/starr/$id")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun unstarr(id: String): SuccessResponse = + maybeResponse( + client.tryToPost(url("/unstarr/$id")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) + } + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) + + suspend fun markAllAsRead(ids: List): SuccessResponse = + maybeResponse( + client.tryToSubmitForm( + url = url("/mark"), + formParameters = + Parameters.build { + if (!shouldHavePostLogin()) { + append("username", appSettingsService.getUserName()) + append("password", appSettingsService.getPassword()) + } + ids.map { append("ids[]", it) } + }, + block = { + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ), + ) suspend fun createSourceForVersion( title: String, @@ -394,7 +564,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { createSource("tags[]", title, url, spout, tags) } else { createSource("tags", title, url, spout, tags) - } + }, ) private suspend fun createSource( @@ -402,28 +572,36 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { title: String, url: String, spout: String, - tags: String + tags: String, ): HttpResponse? = client.tryToSubmitForm( url = url("/source"), - formParameters = Parameters.build { - if (!shouldHavePostLogin()) { - append("username", appSettingsService.getUserName()) - append("password", appSettingsService.getPassword()) - } - append("title", title) - append("url", url) - append("spout", spout) - append(tagsParamName, tags) - }, + formParameters = + Parameters.build { + if (!shouldHavePostLogin()) { + append("username", appSettingsService.getUserName()) + append("password", appSettingsService.getPassword()) + } + append("title", title) + append("url", url) + append("spout", spout) + append(tagsParamName, tags) + }, block = { if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), ) } } - } + }, ) suspend fun updateSourceForVersion( @@ -431,14 +609,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { title: String, url: String, spout: String, - tags: String + tags: String, ): SuccessResponse = maybeResponse( if (appSettingsService.getApiVersion() > 1) { updateSource(id, "tags[]", title, url, spout, tags) } else { updateSource(id, "tags", title, url, spout, tags) - } + }, ) private suspend fun updateSource( @@ -451,44 +629,54 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { ): HttpResponse? = client.tryToSubmitForm( url = url("/source/$id"), - formParameters = Parameters.build { - if (!shouldHavePostLogin()) { - append("username", appSettingsService.getUserName()) - append("password", appSettingsService.getPassword()) - } - append("title", title) - append("url", url) - append("spout", spout) - append(tagsParamName, tags) - }, + formParameters = + Parameters.build { + if (!shouldHavePostLogin()) { + append("username", appSettingsService.getUserName()) + append("password", appSettingsService.getPassword()) + } + append("title", title) + append("url", url) + append("spout", spout) + append(tagsParamName, tags) + }, block = { if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { headers { - append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword())) + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), ) } } - } + }, ) suspend fun deleteSource(id: Int): SuccessResponse = - maybeResponse(client.tryToDelete(url("/source/$id")) { - if (!shouldHavePostLogin()) { - parameter("username", appSettingsService.getUserName()) - parameter("password", appSettingsService.getPassword()) - } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { - headers { - append( - HttpHeaders.Authorization, - constructBasicAuthValue( - BasicAuthCredentials( - username = appSettingsService.getBasicUserName(), - password = appSettingsService.getBasicPassword() - ) - ) - ) + maybeResponse( + client.tryToDelete(url("/source/$id")) { + if (!shouldHavePostLogin()) { + parameter("username", appSettingsService.getUserName()) + parameter("password", appSettingsService.getPassword()) } - } - }) -} \ No newline at end of file + if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + headers { + append( + HttpHeaders.Authorization, + constructBasicAuthValue( + BasicAuthCredentials( + username = appSettingsService.getBasicUserName(), + password = appSettingsService.getBasicPassword(), + ), + ), + ) + } + } + }, + ) +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/ACRASettings.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/ACRASettings.kt index ec0b3d4..c966c4d 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/ACRASettings.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/ACRASettings.kt @@ -13,58 +13,93 @@ class ACRASettings : Settings { // Nothing } - override fun getBoolean(key: String, defaultValue: Boolean): Boolean = false + override fun getBoolean( + key: String, + defaultValue: Boolean, + ): Boolean = false override fun getBooleanOrNull(key: String): Boolean? = null - override fun getDouble(key: String, defaultValue: Double): Double = 0.0 + override fun getDouble( + key: String, + defaultValue: Double, + ): Double = 0.0 override fun getDoubleOrNull(key: String): Double? = null - override fun getFloat(key: String, defaultValue: Float): Float = 0.0F + override fun getFloat( + key: String, + defaultValue: Float, + ): Float = 0.0F override fun getFloatOrNull(key: String): Float? = null - override fun getInt(key: String, defaultValue: Int): Int = 0 + override fun getInt( + key: String, + defaultValue: Int, + ): Int = 0 override fun getIntOrNull(key: String): Int? = null - override fun getLong(key: String, defaultValue: Long): Long = 0 + override fun getLong( + key: String, + defaultValue: Long, + ): Long = 0 override fun getLongOrNull(key: String): Long? = null - override fun getString(key: String, defaultValue: String): String = "0" + override fun getString( + key: String, + defaultValue: String, + ): String = "0" override fun getStringOrNull(key: String): String? = null override fun hasKey(key: String): Boolean = false - override fun putBoolean(key: String, value: Boolean) { + override fun putBoolean( + key: String, + value: Boolean, + ) { // Nothing } - override fun putDouble(key: String, value: Double) { + override fun putDouble( + key: String, + value: Double, + ) { // Nothing } - override fun putFloat(key: String, value: Float) { + override fun putFloat( + key: String, + value: Float, + ) { // Nothing } - override fun putInt(key: String, value: Int) { + override fun putInt( + key: String, + value: Int, + ) { // Nothing } - override fun putLong(key: String, value: Long) { + override fun putLong( + key: String, + value: Long, + ) { // Nothing } - override fun putString(key: String, value: String) { + override fun putString( + key: String, + value: String, + ) { // Nothing } override fun remove(key: String) { // Nothing } - -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt index 4118781..5524fac 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt @@ -3,7 +3,12 @@ package bou.amine.apps.readerforselfossv2.service import com.russhwolf.settings.Settings class AppSettingsService(acraSenderServiceProcess: Boolean = false) { - val settings: Settings = if (acraSenderServiceProcess) { ACRASettings() } else { Settings() } + val settings: Settings = + if (acraSenderServiceProcess) { + ACRASettings() + } else { + Settings() + } // Api related private var _apiVersion: Int = -1 @@ -38,7 +43,6 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { private var _font: String = "" private var _theme: Int? = null - init { refreshApiSettings() refreshUserSettings() @@ -52,7 +56,6 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { return _apiVersion } - fun updateApiVersion(apiMajorVersion: Int) { settings.putInt(API_VERSION_MAJOR, apiMajorVersion) refreshApiVersion() @@ -137,13 +140,13 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { } private fun refreshItemsNumber() { - _itemsNumber = try { - settings.getString(API_ITEMS_NUMBER, "20").toInt() - } catch (e: Exception) { - settings.remove(API_ITEMS_NUMBER) - 20 - } - + _itemsNumber = + try { + settings.getString(API_ITEMS_NUMBER, "20").toInt() + } catch (e: Exception) { + settings.remove(API_ITEMS_NUMBER) + 20 + } } fun getApiTimeout(): Long { @@ -156,18 +159,21 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { private fun secToMs(n: Long) = n * 1000 private fun refreshApiTimeout() { - _apiTimeout = secToMs(try { - val settingsTimeout = settings.getString(API_TIMEOUT, "60") - if (settingsTimeout.toLong() > 0) { - settingsTimeout.toLong() - } else { - settings.remove(API_TIMEOUT) - 60 - } - } catch (e: Exception) { - settings.remove(API_TIMEOUT) - 60 - }) + _apiTimeout = + secToMs( + try { + val settingsTimeout = settings.getString(API_TIMEOUT, "60") + if (settingsTimeout.toLong() > 0) { + settingsTimeout.toLong() + } else { + settings.remove(API_TIMEOUT) + 60 + } + } catch (e: Exception) { + settings.remove(API_TIMEOUT) + 60 + }, + ) } private fun refreshBaseUrl() { @@ -200,6 +206,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { } return _articleViewer == true } + private fun refreshShouldBeCardViewEnabled() { _shouldBeCardView = settings.getBoolean(CARD_VIEW_ACTIVE, false) } @@ -210,6 +217,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { } return _shouldBeCardView == true } + private fun refreshDisplayUnreadCountEnabled() { _displayUnreadCount = settings.getBoolean(DISPLAY_UNREAD_COUNT, true) } @@ -220,6 +228,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { } return _displayUnreadCount == true } + private fun refreshDisplayAllCountEnabled() { _displayAllCount = settings.getBoolean(DISPLAY_OTHER_COUNT, false) } @@ -230,6 +239,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { } return _displayAllCount == true } + private fun refreshFullHeightCardsEnabled() { _fullHeightCards = settings.getBoolean(FULL_HEIGHT_CARDS, false) } @@ -240,6 +250,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { } return _fullHeightCards == true } + private fun refreshUpdateSourcesEnabled() { _updateSources = settings.getBoolean(UPDATE_SOURCES, true) } @@ -250,6 +261,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { } return _updateSources == true } + private fun refreshPeriodicRefreshEnabled() { _periodicRefresh = settings.getBoolean(PERIODIC_REFRESH, false) } @@ -319,7 +331,6 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { return _notifyNewItems == true } - private fun refreshMarkOnScrollEnabled() { _markOnScroll = settings.getBoolean(MARK_ON_SCROLL, false) } @@ -331,7 +342,6 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { return _markOnScroll == true } - private fun refreshActiveAllignment() { _activeAlignment = settings.getInt(TEXT_ALIGN, JUSTIFY) } @@ -429,7 +439,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { fun refreshLoginInformation( url: String, login: String, - password: String + password: String, ) { val regex = """\/\/(\D+):(\D+)@""".toRegex() val matchResult = regex.find(url) @@ -537,6 +547,5 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) { const val ITEMS_CACHING = "items_caching" const val CURRENT_THEME = "currentMode" - } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt index 7c1c1af..57e9c96 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt @@ -9,7 +9,7 @@ fun TAG.toView(): SelfossModel.Tag = SelfossModel.Tag( this.name, this.color, - this.unread.toInt() + this.unread.toInt(), ) fun SOURCE.toView(): SelfossModel.SourceDetail = @@ -21,7 +21,7 @@ fun SOURCE.toView(): SelfossModel.SourceDetail = this.spout, this.error, this.icon, - if (this.url != null) SelfossModel.SourceParams(this.url) else null + if (this.url != null) SelfossModel.SourceParams(this.url) else null, ) fun SelfossModel.SourceDetail.toEntity(): SOURCE = @@ -32,14 +32,14 @@ fun SelfossModel.SourceDetail.toEntity(): SOURCE = this.spout.orEmpty(), this.error.orEmpty(), this.icon.orEmpty(), - this.params?.url + this.params?.url, ) fun SelfossModel.Tag.toEntity(): TAG = TAG( this.tag, this.color, - this.unread.toLong() + this.unread.toLong(), ) fun ITEM.toView(): SelfossModel.Item = @@ -55,7 +55,7 @@ fun ITEM.toView(): SelfossModel.Item = this.link, this.sourcetitle, this.tags.split(","), - this.author + this.author, ) fun SelfossModel.Item.toEntity(): ITEM = @@ -71,5 +71,5 @@ fun SelfossModel.Item.toEntity(): ITEM = this.link, this.sourcetitle.getHtmlDecoded(), this.tags.joinToString(","), - this.author - ) \ No newline at end of file + this.author, + ) diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/Enums.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/Enums.kt index fd610d2..e5a0ff4 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/Enums.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/Enums.kt @@ -3,9 +3,10 @@ package bou.amine.apps.readerforselfossv2.utils enum class ItemType(val position: Int, val type: String) { UNREAD(1, "unread"), ALL(2, "all"), - STARRED(3, "starred"); + STARRED(3, "starred"), + ; companion object { fun fromInt(value: Int) = values().first { it.position == value } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt index 2751b66..a64957e 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -12,4 +12,8 @@ expect fun SelfossModel.Item.getImages(): ArrayList expect fun SelfossModel.Source.getIcon(baseUrl: String): String -expect fun constructUrl(baseUrl: String, path: String, file: String?): String \ No newline at end of file +expect fun constructUrl( + baseUrl: String, + path: String, + file: String?, +): String diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/StringUtils.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/StringUtils.kt index aa0cebf..7c9f0ca 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/StringUtils.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/StringUtils.kt @@ -1,7 +1,6 @@ package bou.amine.apps.readerforselfossv2.utils -fun String?.isEmptyOrNullOrNullString(): Boolean = - this == null || this == "null" || this.isEmpty() +fun String?.isEmptyOrNullOrNullString(): Boolean = this == null || this == "null" || this.isEmpty() fun String.longHash(): Long { var h = 98764321261L @@ -19,4 +18,4 @@ fun String.toStringUriWithHttp(): String = "http://" + this } else { this - } \ No newline at end of file + } diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt index 0357cc6..06d7b59 100644 --- a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt @@ -7,4 +7,4 @@ actual class DriverFactory { actual fun createDriver(): SqlDriver { return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db") } -} \ No newline at end of file +} diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt index 530528c..0bfd800 100644 --- a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt @@ -3,4 +3,4 @@ package bou.amine.apps.readerforselfossv2.rest import io.ktor.client.engine.cio.CIOEngineConfig actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) { -} \ No newline at end of file +} diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt index f611167..39c6fe0 100644 --- a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt @@ -10,4 +10,4 @@ actual class DateUtils { TODO("Not yet implemented") } } -} \ No newline at end of file +} diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt index 9f66d23..6ac9c23 100644 --- a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -22,6 +22,10 @@ actual fun SelfossModel.Source.getIcon(baseUrl: String): String { TODO("Not yet implemented") } -actual fun constructUrl(baseUrl: String, path: String, file: String?): String { +actual fun constructUrl( + baseUrl: String, + path: String, + file: String?, +): String { TODO("Not yet implemented") -} \ No newline at end of file +} diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt index 0357cc6..06d7b59 100644 --- a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt @@ -7,4 +7,4 @@ actual class DriverFactory { actual fun createDriver(): SqlDriver { return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db") } -} \ No newline at end of file +} diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt index 530528c..0bfd800 100644 --- a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/rest/setupInsecureHTTPEngine.kt @@ -3,4 +3,4 @@ package bou.amine.apps.readerforselfossv2.rest import io.ktor.client.engine.cio.CIOEngineConfig actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) { -} \ No newline at end of file +} diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt index df6a812..39c6fe0 100644 --- a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt @@ -1,7 +1,5 @@ package bou.amine.apps.readerforselfossv2.utils -import bou.amine.apps.readerforselfossv2.service.AppSettingsService - actual class DateUtils { actual companion object { actual fun parseDate(dateString: String): Long { @@ -12,5 +10,4 @@ actual class DateUtils { TODO("Not yet implemented") } } - -} \ No newline at end of file +} diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt index 9f66d23..6ac9c23 100644 --- a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -22,6 +22,10 @@ actual fun SelfossModel.Source.getIcon(baseUrl: String): String { TODO("Not yet implemented") } -actual fun constructUrl(baseUrl: String, path: String, file: String?): String { +actual fun constructUrl( + baseUrl: String, + path: String, + file: String?, +): String { TODO("Not yet implemented") -} \ No newline at end of file +}