Debug trying to fix context issues. (#174)
Reviewed-on: #174 Co-authored-by: Amine <amine.bouabdallaoui@pm.me> Co-committed-by: Amine <amine.bouabdallaoui@pm.me>
This commit is contained in:
@ -49,7 +49,12 @@ import org.kodein.di.instance
|
||||
import java.security.MessageDigest
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
|
||||
private const val MIN_WIDTH_CARD_DP = 300
|
||||
|
||||
class HomeActivity :
|
||||
AppCompatActivity(),
|
||||
SearchView.OnQueryTextListener,
|
||||
DIAware {
|
||||
private var items: ArrayList<SelfossModel.Item> = ArrayList()
|
||||
|
||||
private var elementsShown: ItemType = ItemType.UNREAD
|
||||
@ -171,11 +176,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
getElementsAccordingToTab()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
"Found null when swiping at positon $position.",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
"Found null when swiping at positon $position.",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,19 +202,23 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("detekt:LongMethod")
|
||||
private fun handleBottomBar() {
|
||||
tabNewBadge =
|
||||
TextBadgeItem()
|
||||
.setText("")
|
||||
.setHideOnSelect(false).hide(false)
|
||||
.setHideOnSelect(false)
|
||||
.hide(false)
|
||||
tabArchiveBadge =
|
||||
TextBadgeItem()
|
||||
.setText("")
|
||||
.setHideOnSelect(false).hide(false)
|
||||
.setHideOnSelect(false)
|
||||
.hide(false)
|
||||
tabStarredBadge =
|
||||
TextBadgeItem()
|
||||
.setText("")
|
||||
.setHideOnSelect(false).hide(false)
|
||||
.setHideOnSelect(false)
|
||||
.hide(false)
|
||||
|
||||
if (appSettingsService.isDisplayUnreadCountEnabled()) {
|
||||
lifecycleScope.launch {
|
||||
@ -236,14 +246,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_tab_fiber_new_black_24dp,
|
||||
getString(R.string.tab_new),
|
||||
)
|
||||
.setBadgeItem(tabNewBadge)
|
||||
).setBadgeItem(tabNewBadge)
|
||||
val tabArchive =
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_tab_archive_black_24dp,
|
||||
getString(R.string.tab_read),
|
||||
)
|
||||
.setBadgeItem(tabArchiveBadge)
|
||||
).setBadgeItem(tabArchiveBadge)
|
||||
val tabStarred =
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_tab_favorite_black_24dp,
|
||||
@ -277,7 +285,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
|
||||
handleBottomBarActions()
|
||||
|
||||
handleGDPRDialog(appSettingsService.settings.getBoolean("GDPR_shown", false))
|
||||
handleGdprDialog(appSettingsService.settings.getBoolean("GDPR_shown", false))
|
||||
|
||||
handleRecurringTask()
|
||||
CountingIdlingResourceSingleton.increment()
|
||||
@ -289,10 +297,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
getElementsAccordingToTab()
|
||||
}
|
||||
|
||||
private fun handleGDPRDialog(GDPRShown: Boolean) {
|
||||
private fun handleGdprDialog(gdprShown: Boolean) {
|
||||
val messageDigest: MessageDigest = MessageDigest.getInstance("SHA-256")
|
||||
messageDigest.update(appSettingsService.getBaseUrl().toByteArray())
|
||||
if (!GDPRShown) {
|
||||
if (!gdprShown) {
|
||||
val alertDialog = AlertDialog.Builder(this).create()
|
||||
alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
|
||||
alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
|
||||
@ -425,17 +433,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||
}
|
||||
|
||||
private fun getLastVisibleItem(): Int {
|
||||
return when (val manager = binding.recyclerView.layoutManager) {
|
||||
private fun getLastVisibleItem(): Int =
|
||||
when (val manager = binding.recyclerView.layoutManager) {
|
||||
is StaggeredGridLayoutManager ->
|
||||
manager.findLastCompletelyVisibleItemPositions(
|
||||
null,
|
||||
).last()
|
||||
manager
|
||||
.findLastCompletelyVisibleItemPositions(
|
||||
null,
|
||||
).last()
|
||||
|
||||
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun mayBeEmpty() =
|
||||
if (items.isEmpty()) {
|
||||
@ -538,7 +546,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
private fun calculateNoOfColumns(): Int {
|
||||
val displayMetrics = resources.displayMetrics
|
||||
val dpWidth = displayMetrics.widthPixels / displayMetrics.density
|
||||
return (dpWidth / 300).toInt()
|
||||
return (dpWidth / MIN_WIDTH_CARD_DP).toInt()
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(p0: String?): Boolean {
|
||||
@ -577,7 +585,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
messageRes: Int,
|
||||
doFn: () -> Unit,
|
||||
) {
|
||||
AlertDialog.Builder(this@HomeActivity)
|
||||
AlertDialog
|
||||
.Builder(this@HomeActivity)
|
||||
.setMessage(messageRes)
|
||||
.setTitle(titleRes)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> doFn() }
|
||||
@ -586,10 +595,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
.show()
|
||||
}
|
||||
|
||||
@Suppress("detekt:ReturnCount", "detekt:LongMethod")
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.issue_tracker -> {
|
||||
baseContext.openUrlInBrowser(AppSettingsService.trackerUrl)
|
||||
baseContext.openUrlInBrowser(AppSettingsService.BUG_URL)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -606,18 +616,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val updatedRemote = repository.updateRemote()
|
||||
if (updatedRemote) {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_success_response,
|
||||
Toast.LENGTH_LONG,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_success_response,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_failer_message,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_failer_message,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
CountingIdlingResourceSingleton.decrement()
|
||||
}
|
||||
@ -633,25 +644,26 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val success = repository.markAllAsRead(items)
|
||||
if (success) {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
tabNewBadge.removeBadge()
|
||||
|
||||
getElementsAccordingToTab()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_not_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_not_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
handleListResult()
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
CountingIdlingResourceSingleton.decrement()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -661,7 +673,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
R.id.action_disconnect -> {
|
||||
needsConfirmation(
|
||||
R.string.confirm_disconnect_title,
|
||||
R.string.confirm_disconnect_description
|
||||
R.string.confirm_disconnect_description,
|
||||
) {
|
||||
runBlocking {
|
||||
repository.logout()
|
||||
@ -702,7 +714,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
private fun handleRecurringTask() {
|
||||
if (appSettingsService.isPeriodicRefreshEnabled()) {
|
||||
val myConstraints =
|
||||
Constraints.Builder()
|
||||
Constraints
|
||||
.Builder()
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
|
||||
.setRequiresStorageNotLow(true)
|
||||
@ -711,19 +724,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
val backgroundWork =
|
||||
PeriodicWorkRequestBuilder<LoadingWorker>(
|
||||
appSettingsService.getRefreshMinutes(),
|
||||
TimeUnit.MINUTES
|
||||
)
|
||||
.setConstraints(myConstraints)
|
||||
TimeUnit.MINUTES,
|
||||
).setConstraints(myConstraints)
|
||||
.addTag("selfoss-loading")
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(
|
||||
baseContext,
|
||||
).enqueueUniquePeriodicWork(
|
||||
"selfoss-loading",
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
backgroundWork
|
||||
)
|
||||
WorkManager
|
||||
.getInstance(
|
||||
baseContext,
|
||||
).enqueueUniquePeriodicWork(
|
||||
"selfoss-loading",
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
backgroundWork,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,9 @@ class ImageActivity : AppCompatActivity() {
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
|
||||
private inner class ScreenSlidePagerAdapter(
|
||||
fa: FragmentActivity,
|
||||
) : FragmentStateAdapter(fa) {
|
||||
override fun getItemCount(): Int = allImages.size
|
||||
|
||||
override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position])
|
||||
|
@ -30,7 +30,11 @@ import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class LoginActivity : AppCompatActivity(), DIAware {
|
||||
private const val MAX_INVALID_LOGIN_BEFORE_ALERT_DISPLAYED = 3
|
||||
|
||||
class LoginActivity :
|
||||
AppCompatActivity(),
|
||||
DIAware {
|
||||
private var inValidCount: Int = 0
|
||||
private var isWithLogin = false
|
||||
|
||||
@ -108,7 +112,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
repository.updateApiInformation()
|
||||
ACRA.errorReporter.putCustomData(
|
||||
"SELFOSS_API_VERSION",
|
||||
appSettingsService.getApiVersion().toString()
|
||||
appSettingsService.getApiVersion().toString(),
|
||||
)
|
||||
CountingIdlingResourceSingleton.decrement()
|
||||
}
|
||||
@ -132,9 +136,18 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
binding.passwordView.error = null
|
||||
|
||||
// Store values at the time of the login attempt.
|
||||
val url = binding.urlView.text.toString().trim()
|
||||
val login = binding.loginView.text.toString().trim()
|
||||
val password = binding.passwordView.text.toString().trim()
|
||||
val url =
|
||||
binding.urlView.text
|
||||
.toString()
|
||||
.trim()
|
||||
val login =
|
||||
binding.loginView.text
|
||||
.toString()
|
||||
.trim()
|
||||
val password =
|
||||
binding.passwordView.text
|
||||
.toString()
|
||||
.trim()
|
||||
|
||||
failInvalidUrl(url)
|
||||
failLoginDetails(password, login)
|
||||
@ -151,11 +164,12 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
repository.updateApiInformation()
|
||||
} catch (e: Exception) {
|
||||
if (e.message?.startsWith("No transformation found") == true) {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
R.string.application_selfoss_only,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
applicationContext,
|
||||
R.string.application_selfoss_only,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
preferenceError()
|
||||
showProgress(false)
|
||||
}
|
||||
@ -205,7 +219,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
cancel = true
|
||||
binding.urlView.error = getString(R.string.login_url_problem)
|
||||
inValidCount++
|
||||
if (inValidCount == 3) {
|
||||
if (inValidCount == MAX_INVALID_LOGIN_BEFORE_ALERT_DISPLAYED) {
|
||||
val alertDialog = AlertDialog.Builder(this).create()
|
||||
alertDialog.setTitle(getString(R.string.warning_wrong_url))
|
||||
alertDialog.setMessage(getString(R.string.text_wrong_url))
|
||||
@ -270,7 +284,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
return when (item.itemId) {
|
||||
R.id.issue_tracker -> {
|
||||
val browserIntent =
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.BUG_URL))
|
||||
startActivity(browserIntent)
|
||||
return true
|
||||
}
|
||||
@ -280,9 +294,9 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.withAboutSpecial2("Bug reports")
|
||||
.withAboutSpecial2Description(AppSettingsService.trackerUrl)
|
||||
.withAboutSpecial2Description(AppSettingsService.BUG_URL)
|
||||
.withAboutSpecial1("Project Page")
|
||||
.withAboutSpecial1Description(AppSettingsService.sourceUrl)
|
||||
.withAboutSpecial1Description(AppSettingsService.SOURCE_URL)
|
||||
.start(this)
|
||||
true
|
||||
}
|
||||
@ -290,4 +304,4 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
||||
import bou.amine.apps.readerforselfossv2.android.testing.TestingHelper
|
||||
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
||||
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
||||
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
||||
import bou.amine.apps.readerforselfossv2.di.networkModule
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||
import com.github.ln_12.library.ConnectivityStatus
|
||||
@ -36,21 +36,23 @@ import org.kodein.di.bind
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.di.singleton
|
||||
|
||||
class MyApp : MultiDexApplication(), DIAware {
|
||||
class MyApp :
|
||||
MultiDexApplication(),
|
||||
DIAware {
|
||||
override val di by DI.lazy {
|
||||
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess() || TestingHelper().isUnitTest()) }
|
||||
import(networkModule)
|
||||
bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
|
||||
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
|
||||
bind<Repository>() with
|
||||
singleton {
|
||||
Repository(
|
||||
instance(),
|
||||
instance(),
|
||||
isConnectionAvailable,
|
||||
instance(),
|
||||
)
|
||||
}
|
||||
singleton {
|
||||
Repository(
|
||||
instance(),
|
||||
instance(),
|
||||
isConnectionAvailable,
|
||||
instance(),
|
||||
)
|
||||
}
|
||||
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
|
||||
bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
|
||||
}
|
||||
@ -60,6 +62,7 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
private val connectivityStatus: ConnectivityStatus by instance()
|
||||
private val driverFactory: DriverFactory by instance()
|
||||
|
||||
@Suppress("detekt:ForbiddenComment")
|
||||
// TODO: handle with the "previous" way
|
||||
private val isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(true)
|
||||
|
||||
@ -89,11 +92,12 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
R.string.network_connectivity_lost
|
||||
}
|
||||
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
toastMessage,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
applicationContext,
|
||||
toastMessage,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,13 +155,13 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
|
||||
val name = getString(R.string.notification_channel_sync)
|
||||
val importance = NotificationManager.IMPORTANCE_LOW
|
||||
val mChannel = NotificationChannel(AppSettingsService.syncChannelId, name, importance)
|
||||
val mChannel = NotificationChannel(AppSettingsService.SYNC_CHANNEL_ID, name, importance)
|
||||
|
||||
val newItemsChannelname = getString(R.string.new_items_channel_sync)
|
||||
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
|
||||
val newItemsChannelmChannel =
|
||||
NotificationChannel(
|
||||
AppSettingsService.newItemsChannelId,
|
||||
AppSettingsService.NEW_ITEMS_CHANNEL,
|
||||
newItemsChannelname,
|
||||
newItemsChannelimportance,
|
||||
)
|
||||
@ -199,4 +203,4 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
super.onPause(owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,9 @@ import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
class ReaderActivity :
|
||||
AppCompatActivity(),
|
||||
DIAware {
|
||||
private var currentItem: Int = 0
|
||||
|
||||
private lateinit var toolbarMenu: Menu
|
||||
@ -51,6 +53,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
showMenuItem(false)
|
||||
}
|
||||
|
||||
@Suppress("detekt:SwallowedException")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityReaderBinding.inflate(layoutInflater)
|
||||
@ -99,8 +102,9 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
oldInstanceState.clear()
|
||||
}
|
||||
|
||||
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) :
|
||||
FragmentStateAdapter(fa) {
|
||||
private inner class ScreenSlidePagerAdapter(
|
||||
fa: FragmentActivity,
|
||||
) : FragmentStateAdapter(fa) {
|
||||
override fun getItemCount(): Int = allItems.size
|
||||
|
||||
override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position])
|
||||
@ -109,25 +113,26 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
override fun onKeyDown(
|
||||
keyCode: Int,
|
||||
event: KeyEvent?,
|
||||
): Boolean {
|
||||
return when (keyCode) {
|
||||
): Boolean =
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||
val currentFragment =
|
||||
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
||||
currentFragment.scrollDown()
|
||||
currentFragment.volumeButtonScrollDown()
|
||||
true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||
val currentFragment =
|
||||
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
||||
currentFragment.scrollUp()
|
||||
currentFragment.volumeButtonScrollUp()
|
||||
true
|
||||
}
|
||||
|
||||
else -> {
|
||||
super.onKeyDown(keyCode, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun alignmentMenu() {
|
||||
val showJustify = appSettingsService.getActiveAllignment() == AppSettingsService.ALIGN_LEFT
|
||||
@ -187,6 +192,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.star -> {
|
||||
if (allItems[binding.pager.currentItem].starred) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
@ -200,10 +206,12 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
afterSave()
|
||||
}
|
||||
}
|
||||
|
||||
R.id.align_left -> {
|
||||
switchAlignmentSetting(AppSettingsService.ALIGN_LEFT)
|
||||
refreshFragment()
|
||||
}
|
||||
|
||||
R.id.align_justify -> {
|
||||
switchAlignmentSetting(AppSettingsService.JUSTIFY)
|
||||
refreshFragment()
|
||||
|
@ -18,7 +18,9 @@ import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class SourcesActivity : AppCompatActivity(), DIAware {
|
||||
class SourcesActivity :
|
||||
AppCompatActivity(),
|
||||
DIAware {
|
||||
private lateinit var binding: ActivitySourcesBinding
|
||||
|
||||
override val di by closestDI()
|
||||
@ -68,11 +70,12 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
||||
binding.recyclerView.adapter = mAdapter
|
||||
mAdapter.notifyDataSetChanged()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@SourcesActivity,
|
||||
R.string.cant_get_sources,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@SourcesActivity,
|
||||
R.string.cant_get_sources,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
CountingIdlingResourceSingleton.decrement()
|
||||
}
|
||||
@ -81,4 +84,4 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
||||
startActivity(Intent(this@SourcesActivity, UpsertSourceActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
||||
class UpsertSourceActivity :
|
||||
AppCompatActivity(),
|
||||
DIAware {
|
||||
private var existingSource: SelfossModel.SourceDetail? = null
|
||||
private var mSpoutsValue: String? = null
|
||||
|
||||
@ -83,6 +85,7 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("detekt:SwallowedException")
|
||||
private fun handleSpoutsSpinner() {
|
||||
val spoutsKV = HashMap<String, String>()
|
||||
binding.spoutsSpinner.onItemSelectedListener =
|
||||
@ -105,11 +108,12 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
||||
}
|
||||
|
||||
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,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@UpsertSourceActivity,
|
||||
if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
binding.progress.visibility = View.GONE
|
||||
}
|
||||
|
||||
@ -170,6 +174,7 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
||||
sourceDetailsUnavailable -> {
|
||||
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
else -> {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val successfullyAddedSource =
|
||||
@ -192,11 +197,12 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
||||
if (successfullyAddedSource) {
|
||||
finish()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@UpsertSourceActivity,
|
||||
R.string.cant_create_source,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@UpsertSourceActivity,
|
||||
R.string.cant_create_source,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,10 @@ class ItemCardAdapter(
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
private fun handleClickListeners(holderBinding: CardItemBinding, position: Int) {
|
||||
private fun handleClickListeners(
|
||||
holderBinding: CardItemBinding,
|
||||
position: Int,
|
||||
) {
|
||||
holderBinding.favButton.setOnClickListener {
|
||||
val item = items[position]
|
||||
if (item.starred) {
|
||||
@ -96,12 +99,13 @@ class ItemCardAdapter(
|
||||
|
||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||
|
||||
binding.sourceTitleAndDate.text = try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
binding.sourceTitleAndDate.text =
|
||||
try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
|
||||
if (!appSettingsService.isFullHeightCardsEnabled()) {
|
||||
binding.itemImage.maxHeight = imageMaxHeight
|
||||
@ -125,5 +129,7 @@ class ItemCardAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
inner class ViewHolder(
|
||||
val binding: CardItemBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
@ -53,12 +53,13 @@ class ItemListAdapter(
|
||||
|
||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||
|
||||
binding.sourceTitleAndDate.text = try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
binding.sourceTitleAndDate.text =
|
||||
try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
|
||||
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
|
||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||
@ -72,5 +73,7 @@ class ItemListAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
inner class ViewHolder(
|
||||
val binding: ListItemBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
@ -18,7 +18,9 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.DIAware
|
||||
|
||||
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>(), DIAware {
|
||||
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> :
|
||||
RecyclerView.Adapter<VH>(),
|
||||
DIAware {
|
||||
abstract val items: ArrayList<SelfossModel.Item>
|
||||
abstract val repository: Repository
|
||||
abstract val binding: ViewBinding
|
||||
@ -45,8 +47,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
app.findViewById(R.id.coordLayout),
|
||||
R.string.marked_as_read,
|
||||
Snackbar.LENGTH_LONG,
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
).setAction(R.string.undo_string) {
|
||||
unreadItemAtIndex(item, position, false)
|
||||
}
|
||||
|
||||
@ -66,8 +67,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
app.findViewById(R.id.coordLayout),
|
||||
R.string.marked_as_unread,
|
||||
Snackbar.LENGTH_LONG,
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
).setAction(R.string.undo_string) {
|
||||
readItemAtIndex(item, position, false)
|
||||
}
|
||||
|
||||
@ -77,7 +77,10 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
s.show()
|
||||
}
|
||||
|
||||
protected fun handleLinkOpening(holderBinding: ViewBinding, position: Int) {
|
||||
protected fun handleLinkOpening(
|
||||
holderBinding: ViewBinding,
|
||||
position: Int,
|
||||
) {
|
||||
holderBinding.root.setOnClickListener {
|
||||
repository.setReaderItems(items)
|
||||
c.openItemUrl(
|
||||
|
@ -29,7 +29,8 @@ import org.kodein.di.instance
|
||||
class SourcesListAdapter(
|
||||
private val app: Activity,
|
||||
private val items: ArrayList<SelfossModel.SourceDetail>,
|
||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(), DIAware {
|
||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(),
|
||||
DIAware {
|
||||
private val c: Context = app.baseContext
|
||||
private lateinit var binding: SourceListItemBinding
|
||||
|
||||
@ -61,11 +62,12 @@ class SourcesListAdapter(
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
app,
|
||||
R.string.can_delete_source,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
app,
|
||||
R.string.can_delete_source,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,5 +101,7 @@ class SourcesListAdapter(
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView)
|
||||
inner class ViewHolder(
|
||||
val mView: ConstraintLayout,
|
||||
) : RecyclerView.ViewHolder(mView)
|
||||
}
|
||||
|
@ -23,11 +23,15 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.instance
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
class LoadingWorker(val context: Context, params: WorkerParameters) :
|
||||
Worker(context, params),
|
||||
private const val NOTIFICATION_DELAY = 4000L
|
||||
|
||||
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()
|
||||
@ -40,12 +44,13 @@ class LoadingWorker(val context: Context, params: WorkerParameters) :
|
||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
val notification =
|
||||
NotificationCompat.Builder(applicationContext, AppSettingsService.syncChannelId)
|
||||
NotificationCompat
|
||||
.Builder(applicationContext, AppSettingsService.SYNC_CHANNEL_ID)
|
||||
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||
.setContentText(context.getString(R.string.loading_notification_text))
|
||||
.setOngoing(true)
|
||||
.setPriority(PRIORITY_LOW)
|
||||
.setChannelId(AppSettingsService.syncChannelId)
|
||||
.setChannelId(AppSettingsService.SYNC_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
||||
|
||||
notificationManager.notify(1, notification.build())
|
||||
@ -87,28 +92,27 @@ class LoadingWorker(val context: Context, params: WorkerParameters) :
|
||||
PendingIntent.getActivity(context, 0, intent, pflags)
|
||||
|
||||
val newItemsNotification =
|
||||
NotificationCompat.Builder(
|
||||
applicationContext,
|
||||
AppSettingsService.newItemsChannelId,
|
||||
)
|
||||
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||
NotificationCompat
|
||||
.Builder(
|
||||
applicationContext,
|
||||
AppSettingsService.NEW_ITEMS_CHANNEL,
|
||||
).setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||
.setContentText(
|
||||
context.getString(
|
||||
R.string.new_items_notification_text,
|
||||
newSize,
|
||||
),
|
||||
)
|
||||
.setPriority(PRIORITY_DEFAULT)
|
||||
.setChannelId(AppSettingsService.newItemsChannelId)
|
||||
).setPriority(PRIORITY_DEFAULT)
|
||||
.setChannelId(AppSettingsService.NEW_ITEMS_CHANNEL)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
||||
|
||||
Timer("", false).schedule(4000) {
|
||||
Timer("", false).schedule(NOTIFICATION_DELAY) {
|
||||
notificationManager.notify(2, newItemsNotification.build())
|
||||
}
|
||||
}
|
||||
Timer("", false).schedule(4000) {
|
||||
Timer("", false).schedule(NOTIFICATION_DELAY) {
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package bou.amine.apps.readerforselfossv2.android.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.TypedArray
|
||||
@ -8,6 +9,7 @@ import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.util.TypedValue.DATA_NULL_UNDEFINED
|
||||
import android.view.GestureDetector
|
||||
import android.view.InflateException
|
||||
import android.view.LayoutInflater
|
||||
@ -64,8 +66,14 @@ import java.util.concurrent.ExecutionException
|
||||
|
||||
private const val IMAGE_JPG = "image/jpg"
|
||||
|
||||
class ArticleFragment : Fragment(), DIAware {
|
||||
private var fontSize: Int = 16
|
||||
private const val WHITE_COLOR_HEX = 0xFFFFFF
|
||||
|
||||
private const val DEFAULT_FONT_SIZE = 16
|
||||
|
||||
class ArticleFragment :
|
||||
Fragment(),
|
||||
DIAware {
|
||||
private var fontSize: Int = DEFAULT_FONT_SIZE
|
||||
private lateinit var item: SelfossModel.Item
|
||||
private lateinit var url: String
|
||||
private lateinit var contentText: String
|
||||
@ -96,6 +104,7 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
item = pi.toModel()
|
||||
}
|
||||
|
||||
@Suppress("detekt:LongMethod")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@ -113,12 +122,13 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
contentText = item.content
|
||||
contentTitle = item.title.getHtmlDecoded()
|
||||
contentImage = item.getThumbnail(repository.baseUrl)
|
||||
contentSource = try {
|
||||
item.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
|
||||
item.sourceAuthorOnly()
|
||||
}
|
||||
contentSource =
|
||||
try {
|
||||
item.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
|
||||
item.sourceAuthorOnly()
|
||||
}
|
||||
allImages = item.getImages()
|
||||
|
||||
fontSize = appSettingsService.getFontSize()
|
||||
@ -164,7 +174,8 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
} catch (e: InflateException) {
|
||||
e.sendSilentlyWithAcraWithName("webview not available")
|
||||
try {
|
||||
AlertDialog.Builder(requireContext())
|
||||
AlertDialog
|
||||
.Builder(requireContext())
|
||||
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
|
||||
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
|
||||
.setPositiveButton(
|
||||
@ -172,8 +183,7 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
) { _, _ ->
|
||||
appSettingsService.disableArticleViewer()
|
||||
requireActivity().finish()
|
||||
}
|
||||
.create()
|
||||
}.create()
|
||||
.show()
|
||||
} catch (e: IllegalStateException) {
|
||||
e.sendSilentlyWithAcraWithName("Context required is null")
|
||||
@ -232,21 +242,23 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
repository.markAsRead(this@ArticleFragment.item)
|
||||
}
|
||||
this@ArticleFragment.item.unread = false
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
R.string.marked_as_read,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
requireContext(),
|
||||
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()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
R.string.marked_as_unread,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
e.sendSilentlyWithAcraWithName("Context required is null")
|
||||
@ -273,6 +285,7 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("detekt:SwallowedException")
|
||||
private fun getContentFromMercury() {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
|
||||
@ -311,16 +324,15 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLeadImage(lead_image_url: String?) {
|
||||
if (!lead_image_url.isNullOrEmpty() && context != null) {
|
||||
private fun handleLeadImage(leadImageUrl: String?) {
|
||||
if (!leadImageUrl.isNullOrEmpty() && context != null) {
|
||||
binding.imageView.visibility = View.VISIBLE
|
||||
Glide
|
||||
.with(requireContext())
|
||||
.asBitmap()
|
||||
.load(
|
||||
lead_image_url,
|
||||
)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
leadImageUrl,
|
||||
).apply(RequestOptions.fitCenterTransform())
|
||||
.into(binding.imageView)
|
||||
} else {
|
||||
binding.imageView.visibility = View.GONE
|
||||
@ -334,84 +346,133 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
url: String,
|
||||
): Boolean {
|
||||
return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||
): Boolean =
|
||||
if (context != null &&
|
||||
url.isUrlValid() &&
|
||||
binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
||||
) {
|
||||
requireContext().openUrlInBrowser(url)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("detekt:LongMethod", "detekt:SwallowedException")
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun shouldInterceptRequest(
|
||||
view: WebView,
|
||||
url: String,
|
||||
): WebResourceResponse? {
|
||||
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||
var glideResource: WebResourceResponse? = null
|
||||
if (url.lowercase(Locale.US).contains(".jpg") ||
|
||||
url.lowercase(Locale.US)
|
||||
url
|
||||
.lowercase(Locale.US)
|
||||
.contains(".jpeg")
|
||||
) {
|
||||
try {
|
||||
val image =
|
||||
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit()
|
||||
Glide
|
||||
.with(view)
|
||||
.asBitmap()
|
||||
.apply(glideOptions)
|
||||
.load(url)
|
||||
.submit()
|
||||
.get()
|
||||
return WebResourceResponse(
|
||||
IMAGE_JPG,
|
||||
"UTF-8",
|
||||
getBitmapInputStream(image, Bitmap.CompressFormat.JPEG),
|
||||
)
|
||||
glideResource =
|
||||
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()
|
||||
Glide
|
||||
.with(view)
|
||||
.asBitmap()
|
||||
.apply(glideOptions)
|
||||
.load(url)
|
||||
.submit()
|
||||
.get()
|
||||
return WebResourceResponse(
|
||||
IMAGE_JPG,
|
||||
"UTF-8",
|
||||
getBitmapInputStream(image, Bitmap.CompressFormat.PNG),
|
||||
)
|
||||
glideResource =
|
||||
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()
|
||||
Glide
|
||||
.with(view)
|
||||
.asBitmap()
|
||||
.apply(glideOptions)
|
||||
.load(url)
|
||||
.submit()
|
||||
.get()
|
||||
return WebResourceResponse(
|
||||
IMAGE_JPG,
|
||||
"UTF-8",
|
||||
getBitmapInputStream(image, Bitmap.CompressFormat.WEBP),
|
||||
)
|
||||
glideResource =
|
||||
WebResourceResponse(
|
||||
IMAGE_JPG,
|
||||
"UTF-8",
|
||||
getBitmapInputStream(image, Bitmap.CompressFormat.WEBP),
|
||||
)
|
||||
} catch (e: ExecutionException) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
return super.shouldInterceptRequest(view, url)
|
||||
return glideResource ?: super.shouldInterceptRequest(view, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("detekt:LongMethod", "detekt:ImplicitDefaultLocale")
|
||||
private fun htmlToWebview() {
|
||||
val context: Context
|
||||
try {
|
||||
context = requireContext()
|
||||
} catch (e: IllegalStateException) {
|
||||
e.sendSilentlyWithAcraWithName("Context required is null")
|
||||
return
|
||||
}
|
||||
|
||||
val colorOnSurface = TypedValue()
|
||||
val colorSurface = TypedValue()
|
||||
|
||||
try {
|
||||
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
|
||||
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
|
||||
val a: TypedArray = context.obtainStyledAttributes(resId, attrs)
|
||||
|
||||
binding.webcontent.settings.standardFontFamily = a.getString(0)
|
||||
binding.webcontent.visibility = View.VISIBLE
|
||||
|
||||
val colorOnSurface = TypedValue()
|
||||
requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
|
||||
context.theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
|
||||
|
||||
val colorSurface = TypedValue()
|
||||
requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
|
||||
context.theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
|
||||
} catch (e: IllegalStateException) {
|
||||
e.sendSilentlyWithAcraWithName("Context issue when setting attributes, but context wasn't null before")
|
||||
}
|
||||
|
||||
val colorSurfaceString =
|
||||
String.format(
|
||||
"#%06X",
|
||||
WHITE_COLOR_HEX and (if (colorSurface.data != DATA_NULL_UNDEFINED) colorSurface.data else WHITE_COLOR_HEX),
|
||||
)
|
||||
|
||||
val colorOnSurfaceString =
|
||||
String.format(
|
||||
"#%06X",
|
||||
WHITE_COLOR_HEX and (if (colorOnSurface.data != DATA_NULL_UNDEFINED) colorOnSurface.data else 0),
|
||||
)
|
||||
|
||||
try {
|
||||
binding.webcontent.settings.useWideViewPort = true
|
||||
binding.webcontent.settings.loadWithOverviewMode = true
|
||||
binding.webcontent.settings.javaScriptEnabled = false
|
||||
@ -422,19 +483,25 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
GestureDetector(
|
||||
activity,
|
||||
object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||
return performClick()
|
||||
}
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean = performClick()
|
||||
},
|
||||
)
|
||||
|
||||
binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) }
|
||||
binding.webcontent.setOnTouchListener { _, event ->
|
||||
gestureDetector.onTouchEvent(
|
||||
event,
|
||||
)
|
||||
}
|
||||
|
||||
binding.webcontent.settings.layoutAlgorithm =
|
||||
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||
} catch (e: IllegalStateException) {
|
||||
e.sendSilentlyWithAcraWithName("Context is null but wasn't, and that's causing issues with webview config")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
var baseUrl: String? = null
|
||||
|
||||
try {
|
||||
val itemUrl = URL(url)
|
||||
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
||||
@ -484,12 +551,12 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
| color: ${
|
||||
String.format(
|
||||
"#%06X",
|
||||
0xFFFFFF and resources.getColor(R.color.colorAccent),
|
||||
WHITE_COLOR_HEX and context.resources.getColor(R.color.colorAccent),
|
||||
)
|
||||
} !important;
|
||||
| }
|
||||
| *:not(a) {
|
||||
| color: ${String.format("#%06X", 0xFFFFFF and colorOnSurface.data)};
|
||||
| color: $colorOnSurfaceString;
|
||||
| }
|
||||
| * {
|
||||
| font-size: ${fontSize}px;
|
||||
@ -497,26 +564,11 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
| word-break: break-word;
|
||||
| overflow:hidden;
|
||||
| line-height: 1.5em;
|
||||
| background-color: ${
|
||||
String.format(
|
||||
"#%06X",
|
||||
0xFFFFFF and colorSurface.data,
|
||||
)
|
||||
};
|
||||
| background-color: $colorSurfaceString;
|
||||
| }
|
||||
| body, html {
|
||||
| background-color: ${
|
||||
String.format(
|
||||
"#%06X",
|
||||
0xFFFFFF and colorSurface.data,
|
||||
)
|
||||
} !important;
|
||||
| border-color: ${
|
||||
String.format(
|
||||
"#%06X",
|
||||
0xFFFFFF and colorSurface.data,
|
||||
)
|
||||
} !important;
|
||||
| background-color: $colorSurfaceString !important;
|
||||
| border-color: $colorSurfaceString !important;
|
||||
| padding: 0 !important;
|
||||
| margin: 0 !important;
|
||||
| }
|
||||
@ -526,12 +578,7 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
| pre, code {
|
||||
| white-space: pre-wrap;
|
||||
| width:100%;
|
||||
| background-color: ${
|
||||
String.format(
|
||||
"#%06X",
|
||||
0xFFFFFF and colorSurface.data,
|
||||
)
|
||||
};
|
||||
| background-color: $colorSurfaceString;
|
||||
| }
|
||||
| </style>
|
||||
| $fontLinkAndStyle
|
||||
@ -545,16 +592,16 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
null,
|
||||
)
|
||||
} catch (e: IllegalStateException) {
|
||||
e.sendSilentlyWithAcraWithName("Context required is null")
|
||||
e.sendSilentlyWithAcraWithName("Context required is still null ?")
|
||||
}
|
||||
}
|
||||
|
||||
fun scrollDown() {
|
||||
fun volumeButtonScrollDown() {
|
||||
val height = binding.nestedScrollView.measuredHeight
|
||||
binding.nestedScrollView.smoothScrollBy(0, height / 2)
|
||||
}
|
||||
|
||||
fun scrollUp() {
|
||||
fun volumeButtonScrollUp() {
|
||||
val height = binding.nestedScrollView.measuredHeight
|
||||
binding.nestedScrollView.smoothScrollBy(0, -height / 2)
|
||||
}
|
||||
@ -581,10 +628,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)
|
||||
|
||||
@ -596,4 +644,4 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,11 @@ import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.x.closestDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
||||
private const val DRAWABLE_SIZE = 30
|
||||
|
||||
class FilterSheetFragment :
|
||||
BottomSheetDialogFragment(),
|
||||
DIAware {
|
||||
private lateinit var binding: FilterFragmentBinding
|
||||
override val di: DI by closestDI()
|
||||
private val repository: Repository by instance()
|
||||
@ -80,7 +84,8 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
||||
val c = Chip(context)
|
||||
c.ellipsize = TextUtils.TruncateAt.END
|
||||
|
||||
Glide.with(context)
|
||||
Glide
|
||||
.with(context)
|
||||
.load(source.getIcon(repository.baseUrl))
|
||||
.into(
|
||||
object : ViewTarget<Chip?, Drawable?>(c) {
|
||||
@ -153,8 +158,8 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
||||
}
|
||||
gd.setColor(gdColor)
|
||||
gd.shape = GradientDrawable.RECTANGLE
|
||||
gd.setSize(30, 30)
|
||||
gd.cornerRadius = 30F
|
||||
gd.setSize(DRAWABLE_SIZE, DRAWABLE_SIZE)
|
||||
gd.cornerRadius = DRAWABLE_SIZE.toFloat()
|
||||
c.chipIcon = gd
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
|
||||
@ -190,4 +195,4 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
||||
companion object {
|
||||
const val TAG = "FilterModalBottomSheet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class ImageFragment : Fragment() {
|
||||
private lateinit var imageUrl: String
|
||||
private val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||
private var _binding: FragmentImageBinding? = null
|
||||
private val binding get() = _binding
|
||||
val binding get() = _binding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -31,7 +31,8 @@ class ImageFragment : Fragment() {
|
||||
val view = binding?.root
|
||||
|
||||
binding!!.photoView.visibility = View.VISIBLE
|
||||
Glide.with(requireActivity())
|
||||
Glide
|
||||
.with(requireActivity())
|
||||
.asBitmap()
|
||||
.apply(glideOptions)
|
||||
.load(imageUrl)
|
||||
|
@ -9,17 +9,22 @@ import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
|
||||
private const val PRELOAD_IMAGE_TIMEOUT = 10000
|
||||
|
||||
fun SelfossModel.Item.preloadImages(context: Context): Boolean {
|
||||
val imageUrls = this.getImages()
|
||||
|
||||
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(10000)
|
||||
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(PRELOAD_IMAGE_TIMEOUT)
|
||||
|
||||
try {
|
||||
for (url in imageUrls) {
|
||||
if (URLUtil.isValidUrl(url)) {
|
||||
Glide.with(context).asBitmap()
|
||||
Glide
|
||||
.with(context)
|
||||
.asBitmap()
|
||||
.apply(glideOptions)
|
||||
.load(url).submit()
|
||||
.load(url)
|
||||
.submit()
|
||||
}
|
||||
}
|
||||
} catch (e: Error) {
|
||||
@ -40,4 +45,4 @@ fun String.toTextDrawableString(): String {
|
||||
}
|
||||
}
|
||||
return textDrawable.toString()
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,19 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBin
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.API_ITEMS_NUMBER
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.CURRENT_THEME
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.READER_FONT_SIZE
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
|
||||
private const val TITLE_TAG = "settingsActivityTitle"
|
||||
|
||||
const val MAX_ITEMS_NUMBER = 200
|
||||
|
||||
private const val MIN_ITEMS_NUMBER = 1
|
||||
|
||||
class SettingsActivity :
|
||||
AppCompatActivity(),
|
||||
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
|
||||
@ -61,15 +68,14 @@ class SettingsActivity :
|
||||
outState.putCharSequence(TITLE_TAG, title)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
return if (supportFragmentManager.popBackStackImmediate()) {
|
||||
override fun onSupportNavigateUp(): Boolean =
|
||||
if (supportFragmentManager.popBackStackImmediate()) {
|
||||
supportActionBar?.title = getText(R.string.title_activity_settings)
|
||||
false
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceStartFragment(
|
||||
caller: PreferenceFragmentCompat,
|
||||
@ -78,15 +84,17 @@ class SettingsActivity :
|
||||
// Instantiate the new Fragment
|
||||
val args = pref.extras
|
||||
val fragment =
|
||||
supportFragmentManager.fragmentFactory.instantiate(
|
||||
classLoader,
|
||||
pref.fragment.toString(),
|
||||
).apply {
|
||||
arguments = args
|
||||
setTargetFragment(caller, 0)
|
||||
}
|
||||
supportFragmentManager.fragmentFactory
|
||||
.instantiate(
|
||||
classLoader,
|
||||
pref.fragment.toString(),
|
||||
).apply {
|
||||
arguments = args
|
||||
setTargetFragment(caller, 0)
|
||||
}
|
||||
// Replace the existing Fragment with the new Fragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, fragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
@ -102,10 +110,10 @@ class SettingsActivity :
|
||||
) {
|
||||
setPreferencesFromResource(R.xml.pref_main, rootKey)
|
||||
|
||||
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener =
|
||||
preferenceManager.findPreference<Preference>(CURRENT_THEME)?.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
newValue.toString().toInt()
|
||||
newValue.toString().toInt(),
|
||||
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
||||
true
|
||||
}
|
||||
@ -131,7 +139,7 @@ class SettingsActivity :
|
||||
setPreferencesFromResource(R.xml.pref_general, rootKey)
|
||||
|
||||
val editTextPreference =
|
||||
preferenceManager.findPreference<EditTextPreference>("prefer_api_items_number")
|
||||
preferenceManager.findPreference<EditTextPreference>(API_ITEMS_NUMBER)
|
||||
editTextPreference?.setOnBindEditTextListener { editText ->
|
||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
||||
editText.filters =
|
||||
@ -139,13 +147,14 @@ class SettingsActivity :
|
||||
InputFilter { source, _, _, dest, _, _ ->
|
||||
try {
|
||||
val input: Int = (dest.toString() + source.toString()).toInt()
|
||||
if (input in 1..200) return@InputFilter null
|
||||
if (input in MIN_ITEMS_NUMBER..MAX_ITEMS_NUMBER) return@InputFilter null
|
||||
} catch (nfe: NumberFormatException) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
R.string.items_number_should_be_number,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
activity,
|
||||
R.string.items_number_should_be_number,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
}
|
||||
""
|
||||
},
|
||||
@ -161,7 +170,7 @@ class SettingsActivity :
|
||||
) {
|
||||
setPreferencesFromResource(R.xml.pref_viewer, rootKey)
|
||||
|
||||
val fontSize = preferenceManager.findPreference<EditTextPreference>("reader_font_size")
|
||||
val fontSize = preferenceManager.findPreference<EditTextPreference>(READER_FONT_SIZE)
|
||||
fontSize?.setOnBindEditTextListener { editText ->
|
||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
||||
editText.addTextChangedListener {
|
||||
@ -218,23 +227,6 @@ class SettingsActivity :
|
||||
}
|
||||
}
|
||||
|
||||
class ThemePreferenceFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(
|
||||
savedInstanceState: Bundle?,
|
||||
rootKey: String?,
|
||||
) {
|
||||
setPreferencesFromResource(R.xml.pref_theme, rootKey)
|
||||
|
||||
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
newValue.toString().toInt()
|
||||
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LinksPreferenceFragment : PreferenceFragmentCompat() {
|
||||
private fun openUrl(url: String) {
|
||||
context?.openUrlInBrowser(url)
|
||||
@ -248,19 +240,19 @@ class SettingsActivity :
|
||||
|
||||
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
openUrl(AppSettingsService.trackerUrl)
|
||||
openUrl(AppSettingsService.BUG_URL)
|
||||
true
|
||||
}
|
||||
|
||||
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
openUrl(AppSettingsService.sourceUrl)
|
||||
openUrl(AppSettingsService.SOURCE_URL)
|
||||
false
|
||||
}
|
||||
|
||||
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
openUrl(AppSettingsService.translationUrl)
|
||||
openUrl(AppSettingsService.TRANSLATION_URL)
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -274,4 +266,4 @@ class SettingsActivity :
|
||||
setPreferencesFromResource(R.xml.pref_experimental, rootKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.android.testing
|
||||
import androidx.test.espresso.idling.CountingIdlingResource
|
||||
|
||||
object CountingIdlingResourceSingleton {
|
||||
|
||||
private const val RESOURCE = "GLOBAL"
|
||||
|
||||
@JvmField
|
||||
@ -18,4 +17,4 @@ object CountingIdlingResourceSingleton {
|
||||
countingIdlingResource.decrement()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package bou.amine.apps.readerforselfossv2.android.testing
|
||||
|
||||
import android.os.Build
|
||||
|
||||
|
||||
class TestingHelper {
|
||||
fun isUnitTest(): Boolean {
|
||||
var device = Build.DEVICE
|
||||
@ -16,4 +15,4 @@ class TestingHelper {
|
||||
}
|
||||
return device == "robolectric" && product == "robolectric"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ fun Context.shareLink(
|
||||
sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle)
|
||||
sendIntent.type = "text/plain"
|
||||
startActivity(
|
||||
Intent.createChooser(
|
||||
sendIntent,
|
||||
getString(R.string.share),
|
||||
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
||||
Intent
|
||||
.createChooser(
|
||||
sendIntent,
|
||||
getString(R.string.share),
|
||||
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
||||
)
|
||||
}
|
||||
|
@ -59,7 +59,5 @@ class CircleImageView
|
||||
textView.text = text.toTextDrawableString()
|
||||
}
|
||||
|
||||
private fun colorFromIdentifier(key: String): Int {
|
||||
return colorScheme[abs(key.hashCode()) % colorScheme.size]
|
||||
}
|
||||
private fun colorFromIdentifier(key: String): Int = colorScheme[abs(key.hashCode()) % colorScheme.size]
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
|
||||
|
||||
fun Context.openItemUrl(
|
||||
currentItem: Int,
|
||||
linkDecoded: String,
|
||||
@ -26,11 +25,12 @@ fun Context.openItemUrl(
|
||||
app: Activity,
|
||||
) {
|
||||
if (!linkDecoded.isUrlValid()) {
|
||||
Toast.makeText(
|
||||
this,
|
||||
this.getString(R.string.cant_open_invalid_url),
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this,
|
||||
this.getString(R.string.cant_open_invalid_url),
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
} else {
|
||||
if (articleViewer) {
|
||||
val intent = Intent(this, ReaderActivity::class.java)
|
||||
@ -42,8 +42,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()
|
||||
@ -61,7 +60,6 @@ fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) {
|
||||
}
|
||||
|
||||
fun Context.openUrlInBrowserAsNewTask(url: String) {
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
intent.data = Uri.parse(url)
|
||||
@ -74,13 +72,13 @@ fun Context.openUrlInBrowser(url: String) {
|
||||
this.mayBeStartActivity(intent)
|
||||
}
|
||||
|
||||
@Suppress("detekt:SwallowedException")
|
||||
fun Context.mayBeStartActivity(intent: Intent) {
|
||||
try {
|
||||
this.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LinkOnTouchListener : View.OnTouchListener {
|
||||
@ -122,4 +120,4 @@ class LinkOnTouchListener : View.OnTouchListener {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,4 +6,4 @@ import org.acra.ktx.sendSilentlyWithAcra
|
||||
fun Throwable.sendSilentlyWithAcraWithName(name: String) {
|
||||
ACRA.errorReporter.putCustomData("error_source", name)
|
||||
this.sendSilentlyWithAcra()
|
||||
}
|
||||
}
|
||||
|
@ -8,22 +8,19 @@ import org.acra.config.CoreConfiguration
|
||||
import org.acra.config.ReportingAdministrator
|
||||
import org.acra.data.CrashReportData
|
||||
|
||||
|
||||
@AutoService(ReportingAdministrator::class)
|
||||
class AcraReportingAdministrator : ReportingAdministrator {
|
||||
override fun shouldStartCollecting(
|
||||
context: Context,
|
||||
config: CoreConfiguration,
|
||||
reportBuilder: ReportBuilder
|
||||
): Boolean {
|
||||
return reportBuilder.exception !is DeadSystemException && (reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
|
||||
}
|
||||
reportBuilder: ReportBuilder,
|
||||
): Boolean =
|
||||
reportBuilder.exception !is DeadSystemException &&
|
||||
(reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
|
||||
|
||||
override fun shouldSendReport(
|
||||
context: Context,
|
||||
config: CoreConfiguration,
|
||||
crashReportData: CrashReportData
|
||||
): Boolean {
|
||||
return crashReportData.get("BRAND") != "redroid"
|
||||
}
|
||||
}
|
||||
crashReportData: CrashReportData,
|
||||
): Boolean = crashReportData.get("BRAND") != "redroid"
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ import java.io.InputStream
|
||||
fun Context.bitmapCenterCrop(
|
||||
url: String,
|
||||
iv: ImageView,
|
||||
) = Glide.with(this)
|
||||
) = Glide
|
||||
.with(this)
|
||||
.asBitmap()
|
||||
.load(url)
|
||||
.apply(RequestOptions.centerCropTransform())
|
||||
@ -25,17 +26,20 @@ fun Context.circularDrawable(
|
||||
) {
|
||||
view.textView.text = ""
|
||||
|
||||
Glide.with(this)
|
||||
Glide
|
||||
.with(this)
|
||||
.load(url)
|
||||
.into(view.imageView)
|
||||
}
|
||||
|
||||
private const val BITMAP_INPUT_STREAM_COMPRESSION_QUALITY = 80
|
||||
|
||||
fun getBitmapInputStream(
|
||||
bitmap: Bitmap,
|
||||
compressFormat: Bitmap.CompressFormat,
|
||||
): InputStream {
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
bitmap.compress(compressFormat, 80, byteArrayOutputStream)
|
||||
bitmap.compress(compressFormat, BITMAP_INPUT_STREAM_COMPRESSION_QUALITY, byteArrayOutputStream)
|
||||
val bitmapData: ByteArray = byteArrayOutputStream.toByteArray()
|
||||
return ByteArrayInputStream(bitmapData)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.android.utils.network
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
lateinit var s: Snackbar
|
||||
@ -11,19 +10,13 @@ lateinit var s: Snackbar
|
||||
fun isNetworkAccessible(context: Context): Boolean {
|
||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val network = connectivityManager.activeNetwork ?: return false
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) ?: return false
|
||||
|
||||
return when {
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
else -> false
|
||||
}
|
||||
} else {
|
||||
val network = connectivityManager.activeNetworkInfo ?: return false
|
||||
return network.isConnectedOrConnecting
|
||||
return when {
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AppViewModel(private val repository: Repository) : ViewModel() {
|
||||
class AppViewModel(
|
||||
private val repository: Repository,
|
||||
) : ViewModel() {
|
||||
private val _networkAvailableProvider = MutableSharedFlow<Boolean>()
|
||||
val networkAvailableProvider = _networkAvailableProvider.asSharedFlow()
|
||||
private var wasConnected = true
|
||||
@ -19,11 +21,10 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/ModeTitles"
|
||||
android:entryValues="@array/ModeValues"
|
||||
android:key="currentMode"
|
||||
app:iconSpaceReserved="false"
|
||||
android:title="@string/pref_theme_title"
|
||||
app:useSimpleSummaryProvider="false" />
|
||||
</PreferenceScreen>
|
Reference in New Issue
Block a user