Only do api calls on network available.

This commit is contained in:
Amine 2018-11-01 21:10:00 +01:00
parent 0bb2195bff
commit 9816b20bf6
10 changed files with 434 additions and 386 deletions

View File

@ -204,7 +204,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
recyclerView: RecyclerView, recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder viewHolder: RecyclerView.ViewHolder
): Int = ): Int =
if (elementsShown != UNREAD_SHOWN) { if (elementsShown != UNREAD_SHOWN || !this@HomeActivity.isNetworkAccessible(null)) {
0 0
} else { } else {
super.getSwipeDirs( super.getSwipeDirs(
@ -696,36 +696,40 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
var sources: List<Source>? var sources: List<Source>?
fun sourcesApiCall() { fun sourcesApiCall() {
api.sources.enqueue(object : Callback<List<Source>> { if (this@HomeActivity.isNetworkAccessible(null)) {
override fun onResponse( api.sources.enqueue(object : Callback<List<Source>> {
call: Call<List<Source>>?, override fun onResponse(
response: Response<List<Source>> call: Call<List<Source>>?,
) { response: Response<List<Source>>
sources = response.body() ) {
val apiDrawerData = DrawerData(tags, sources) sources = response.body()
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) { val apiDrawerData = DrawerData(tags, sources)
handleDrawerData(apiDrawerData) if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
handleDrawerData(apiDrawerData)
}
} }
override fun onFailure(call: Call<List<Source>>?, t: Throwable?) {
}
})
}
}
if (this@HomeActivity.isNetworkAccessible(null)) {
api.tags.enqueue(object : Callback<List<Tag>> {
override fun onResponse(
call: Call<List<Tag>>,
response: Response<List<Tag>>
) {
tags = response.body()
sourcesApiCall()
} }
override fun onFailure(call: Call<List<Source>>?, t: Throwable?) { override fun onFailure(call: Call<List<Tag>>?, t: Throwable?) {
sourcesApiCall()
} }
}) })
} }
api.tags.enqueue(object : Callback<List<Tag>> {
override fun onResponse(
call: Call<List<Tag>>,
response: Response<List<Tag>>
) {
tags = response.body()
sourcesApiCall()
}
override fun onFailure(call: Call<List<Tag>>?, t: Throwable?) {
sourcesApiCall()
}
})
} }
drawer.addItem( drawer.addItem(
@ -1128,7 +1132,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
private fun reloadBadges() { private fun reloadBadges() {
if (displayUnreadCount || displayAllCount) { if (this@HomeActivity.isNetworkAccessible(null) && (displayUnreadCount || displayAllCount)) {
api.stats.enqueue(object : Callback<Stats> { api.stats.enqueue(object : Callback<Stats> {
override fun onResponse(call: Call<Stats>, response: Response<Stats>) { override fun onResponse(call: Call<Stats>, response: Response<Stats>) {
if (response.body() != null) { if (response.body() != null) {
@ -1234,33 +1238,37 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.refresh -> { R.id.refresh -> {
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) { if (this@HomeActivity.isNetworkAccessible(null)) {
api.update().enqueue(object : Callback<String> { needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
override fun onResponse( api.update().enqueue(object : Callback<String> {
call: Call<String>, override fun onResponse(
response: Response<String> call: Call<String>,
) { response: Response<String>
Toast.makeText( ) {
this@HomeActivity, Toast.makeText(
R.string.refresh_success_response, Toast.LENGTH_LONG this@HomeActivity,
) R.string.refresh_success_response, Toast.LENGTH_LONG
.show() )
} .show()
}
override fun onFailure(call: Call<String>, t: Throwable) { override fun onFailure(call: Call<String>, t: Throwable) {
Toast.makeText( Toast.makeText(
this@HomeActivity, this@HomeActivity,
R.string.refresh_failer_message, R.string.refresh_failer_message,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
}
return true
} else {
return false
} }
return true
} }
R.id.readAll -> { R.id.readAll -> {
if (elementsShown == UNREAD_SHOWN) { if (elementsShown == UNREAD_SHOWN && this@HomeActivity.isNetworkAccessible(null)) {
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) { needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
val ids = allItems.map { it.id } val ids = allItems.map { it.id }

View File

@ -21,6 +21,7 @@ import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import kotlinx.android.synthetic.main.activity_login.* import kotlinx.android.synthetic.main.activity_login.*
@ -196,45 +197,50 @@ class LoginActivity : AppCompatActivity() {
isWithSelfSignedCert, isWithSelfSignedCert,
isWithSelfSignedCert isWithSelfSignedCert
) )
api.login().enqueue(object : Callback<SuccessResponse> {
private fun preferenceError(t: Throwable) {
editor.remove("url")
editor.remove("login")
editor.remove("httpUserName")
editor.remove("password")
editor.remove("httpPassword")
editor.apply()
urlView.error = getString(R.string.wrong_infos)
loginView.error = getString(R.string.wrong_infos)
passwordView.error = getString(R.string.wrong_infos)
httpLoginView.error = getString(R.string.wrong_infos)
httpPasswordView.error = getString(R.string.wrong_infos)
if (logErrors) {
ACRA.getErrorReporter().maybeHandleSilentException(t, this@LoginActivity)
Toast.makeText(
this@LoginActivity,
t.message,
Toast.LENGTH_LONG
).show()
}
showProgress(false)
}
override fun onResponse( if (this@LoginActivity.isNetworkAccessible(this@LoginActivity.findViewById(R.id.loginForm))) {
call: Call<SuccessResponse>, api.login().enqueue(object : Callback<SuccessResponse> {
response: Response<SuccessResponse> private fun preferenceError(t: Throwable) {
) { editor.remove("url")
if (response.body() != null && response.body()!!.isSuccess) { editor.remove("login")
goToMain() editor.remove("httpUserName")
} else { editor.remove("password")
preferenceError(Exception("No response body...")) editor.remove("httpPassword")
editor.apply()
urlView.error = getString(R.string.wrong_infos)
loginView.error = getString(R.string.wrong_infos)
passwordView.error = getString(R.string.wrong_infos)
httpLoginView.error = getString(R.string.wrong_infos)
httpPasswordView.error = getString(R.string.wrong_infos)
if (logErrors) {
ACRA.getErrorReporter().maybeHandleSilentException(t, this@LoginActivity)
Toast.makeText(
this@LoginActivity,
t.message,
Toast.LENGTH_LONG
).show()
}
showProgress(false)
} }
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onResponse(
preferenceError(t) call: Call<SuccessResponse>,
} response: Response<SuccessResponse>
}) ) {
if (response.body() != null && response.body()!!.isSuccess) {
goToMain()
} else {
preferenceError(Exception("No response body..."))
}
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
preferenceError(t)
}
})
} else {
showProgress(false)
}
} }
} }

View File

@ -24,6 +24,7 @@ import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
import apps.amine.bou.readerforselfoss.utils.succeeded import apps.amine.bou.readerforselfoss.utils.succeeded
import apps.amine.bou.readerforselfoss.utils.toggleStar import apps.amine.bou.readerforselfoss.utils.toggleStar
@ -74,7 +75,7 @@ class ReaderActivity : AppCompatActivity() {
val scoop = Scoop.getInstance() val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, toolBar) scoop.bind(this, Toppings.PRIMARY.value, toolBar)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value) scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
} }
@ -103,7 +104,8 @@ class ReaderActivity : AppCompatActivity() {
readItem(allItems[currentItem]) readItem(allItems[currentItem])
pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity)) pager.adapter =
ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
pager.currentItem = currentItem pager.currentItem = currentItem
} }
@ -132,7 +134,7 @@ class ReaderActivity : AppCompatActivity() {
} }
fun readItem(item: Item) { fun readItem(item: Item) {
if (markOnScroll) { if (this@ReaderActivity.isNetworkAccessible(this@ReaderActivity.findViewById(R.id.reader_activity_view)) && markOnScroll) {
thread { thread {
db.itemsDao().delete(item.toEntity()) db.itemsDao().delete(item.toEntity())
} }
@ -164,7 +166,8 @@ class ReaderActivity : AppCompatActivity() {
db.itemsDao().insertAllItems(item.toEntity()) db.itemsDao().insertAllItems(item.toEntity())
} }
if (debugReadingItems) { if (debugReadingItems) {
ACRA.getErrorReporter().maybeHandleSilentException(t, this@ReaderActivity) ACRA.getErrorReporter()
.maybeHandleSilentException(t, this@ReaderActivity)
} }
} }
} }
@ -191,7 +194,6 @@ class ReaderActivity : AppCompatActivity() {
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) : private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
FragmentStatePagerAdapter(fm) { FragmentStatePagerAdapter(fm) {
override fun getCount(): Int { override fun getCount(): Int {
return allItems.size return allItems.size
} }
@ -203,7 +205,12 @@ class ReaderActivity : AppCompatActivity() {
override fun startUpdate(container: ViewGroup) { override fun startUpdate(container: ViewGroup) {
super.startUpdate(container) super.startUpdate(container)
container.background = ColorDrawable(ContextCompat.getColor(this@ReaderActivity, appColors.colorBackground)) container.background = ColorDrawable(
ContextCompat.getColor(
this@ReaderActivity,
appColors.colorBackground
)
)
} }
} }
@ -228,52 +235,56 @@ class ReaderActivity : AppCompatActivity() {
return true return true
} }
R.id.save -> { R.id.save -> {
api.starrItem(allItems[pager.currentItem].id) if (this@ReaderActivity.isNetworkAccessible(null)) {
.enqueue(object : Callback<SuccessResponse> { api.starrItem(allItems[pager.currentItem].id)
override fun onResponse( .enqueue(object : Callback<SuccessResponse> {
call: Call<SuccessResponse>, override fun onResponse(
response: Response<SuccessResponse> call: Call<SuccessResponse>,
) { response: Response<SuccessResponse>
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar() ) {
notifyAdapter() allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
canRemoveFromFavorite() notifyAdapter()
} canRemoveFromFavorite()
}
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
Toast.makeText( Toast.makeText(
baseContext, baseContext,
R.string.cant_mark_favortie, R.string.cant_mark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
}
} }
R.id.unsave -> { R.id.unsave -> {
api.unstarrItem(allItems[pager.currentItem].id) if (this@ReaderActivity.isNetworkAccessible(null)) {
.enqueue(object : Callback<SuccessResponse> { api.unstarrItem(allItems[pager.currentItem].id)
override fun onResponse( .enqueue(object : Callback<SuccessResponse> {
call: Call<SuccessResponse>, override fun onResponse(
response: Response<SuccessResponse> call: Call<SuccessResponse>,
) { response: Response<SuccessResponse>
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar() ) {
notifyAdapter() allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
canFavorite() notifyAdapter()
} canFavorite()
}
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
Toast.makeText( Toast.makeText(
baseContext, baseContext,
R.string.cant_unmark_favortie, R.string.cant_unmark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
}
} }
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)

View File

@ -13,6 +13,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Source import apps.amine.bou.readerforselfoss.api.selfoss.Source
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import kotlinx.android.synthetic.main.activity_sources.* import kotlinx.android.synthetic.main.activity_sources.*
import retrofit2.Call import retrofit2.Call
@ -66,34 +67,36 @@ class SourcesActivity : AppCompatActivity() {
recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = mLayoutManager recyclerView.layoutManager = mLayoutManager
api.sources.enqueue(object : Callback<List<Source>> { if (this@SourcesActivity.isNetworkAccessible(this@SourcesActivity.findViewById(R.id.recyclerView))) {
override fun onResponse( api.sources.enqueue(object : Callback<List<Source>> {
call: Call<List<Source>>, override fun onResponse(
response: Response<List<Source>> call: Call<List<Source>>,
) { response: Response<List<Source>>
if (response.body() != null && response.body()!!.isNotEmpty()) { ) {
items = response.body() as ArrayList<Source> if (response.body() != null && response.body()!!.isNotEmpty()) {
items = response.body() as ArrayList<Source>
}
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
recyclerView.adapter = mAdapter
mAdapter.notifyDataSetChanged()
if (items.isEmpty()) {
Toast.makeText(
this@SourcesActivity,
R.string.nothing_here,
Toast.LENGTH_SHORT
).show()
}
} }
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
recyclerView.adapter = mAdapter override fun onFailure(call: Call<List<Source>>, t: Throwable) {
mAdapter.notifyDataSetChanged()
if (items.isEmpty()) {
Toast.makeText( Toast.makeText(
this@SourcesActivity, this@SourcesActivity,
R.string.nothing_here, R.string.cant_get_sources,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} })
}
override fun onFailure(call: Call<List<Source>>, t: Throwable) {
Toast.makeText(
this@SourcesActivity,
R.string.cant_get_sources,
Toast.LENGTH_SHORT
).show()
}
})
fab.setOnClickListener { fab.setOnClickListener {
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java)) startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))

View File

@ -21,6 +21,7 @@ import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.openInBrowserAsNewTask import apps.amine.bou.readerforselfoss.utils.openInBrowserAsNewTask
import apps.amine.bou.readerforselfoss.utils.openItemUrl import apps.amine.bou.readerforselfoss.utils.openItemUrl
import apps.amine.bou.readerforselfoss.utils.shareLink import apps.amine.bou.readerforselfoss.utils.shareLink
@ -117,49 +118,53 @@ class ItemCardAdapter(
mView.favButton.setOnLikeListener(object : OnLikeListener { mView.favButton.setOnLikeListener(object : OnLikeListener {
override fun liked(likeButton: LikeButton) { override fun liked(likeButton: LikeButton) {
val (id) = items[adapterPosition] if (c.isNetworkAccessible(null)) {
api.starrItem(id).enqueue(object : Callback<SuccessResponse> { val (id) = items[adapterPosition]
override fun onResponse( api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
call: Call<SuccessResponse>, override fun onResponse(
response: Response<SuccessResponse> call: Call<SuccessResponse>,
) { response: Response<SuccessResponse>
} ) {
}
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
mView.favButton.isLiked = false mView.favButton.isLiked = false
Toast.makeText( Toast.makeText(
c, c,
R.string.cant_mark_favortie, R.string.cant_mark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
}
} }
override fun unLiked(likeButton: LikeButton) { override fun unLiked(likeButton: LikeButton) {
val (id) = items[adapterPosition] if (c.isNetworkAccessible(null)) {
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> { val (id) = items[adapterPosition]
override fun onResponse( api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
call: Call<SuccessResponse>, override fun onResponse(
response: Response<SuccessResponse> call: Call<SuccessResponse>,
) { response: Response<SuccessResponse>
} ) {
}
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
mView.favButton.isLiked = true mView.favButton.isLiked = true
Toast.makeText( Toast.makeText(
c, c,
R.string.cant_unmark_favortie, R.string.cant_unmark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
}
} }
}) })

View File

@ -13,6 +13,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
import apps.amine.bou.readerforselfoss.utils.succeeded import apps.amine.bou.readerforselfoss.utils.succeeded
import org.acra.ACRA import org.acra.ACRA
@ -45,30 +46,32 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG
) )
.setAction(R.string.undo_string) { .setAction(R.string.undo_string) {
items.add(position, i) if (app.isNetworkAccessible(null)) {
thread { items.add(position, i)
db.itemsDao().insertAllItems(i.toEntity()) thread {
} db.itemsDao().insertAllItems(i.toEntity())
notifyItemInserted(position)
updateItems(items)
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
} }
notifyItemInserted(position)
updateItems(items)
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
items.remove(i) override fun onResponse(
thread { call: Call<SuccessResponse>,
db.itemsDao().delete(i.toEntity()) response: Response<SuccessResponse>
) {
} }
notifyItemRemoved(position)
updateItems(items) override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
doUnmark(i, position) items.remove(i)
} thread {
}) db.itemsDao().delete(i.toEntity())
}
notifyItemRemoved(position)
updateItems(items)
doUnmark(i, position)
}
})
}
} }
val view = s.view val view = s.view
@ -78,60 +81,61 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
} }
fun removeItemAtIndex(position: Int) { fun removeItemAtIndex(position: Int) {
if (app.isNetworkAccessible(null)) {
val i = items[position]
val i = items[position] items.remove(i)
notifyItemRemoved(position)
updateItems(items)
items.remove(i) // TODO: Handle network status.
notifyItemRemoved(position) // IF offline, delete from cached articles, and add to some table that will replay the calls on network activation.
updateItems(items)
// TODO: Handle network status. thread {
// IF offline, delete from cached articles, and add to some table that will replay the calls on network activation. db.itemsDao().delete(i.toEntity())
}
thread { api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
db.itemsDao().delete(i.toEntity()) override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) {
val message =
"message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " +
"response message: ${response.message()} " +
"response errorBody: ${response.errorBody()?.string()} " +
"body success: ${response.body()?.success} " +
"body isSuccess: ${response.body()?.isSuccess}"
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
}
doUnmark(i, position)
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
if (debugReadingItems) {
ACRA.getErrorReporter().maybeHandleSilentException(t, app)
Toast.makeText(app.baseContext, t.message, Toast.LENGTH_LONG).show()
}
Toast.makeText(
app,
app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT
).show()
items.add(i)
notifyItemInserted(position)
updateItems(items)
thread {
db.itemsDao().insertAllItems(i.toEntity())
}
}
})
} }
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) {
val message =
"message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " +
"response message: ${response.message()} " +
"response errorBody: ${response.errorBody()?.string()} " +
"body success: ${response.body()?.success} " +
"body isSuccess: ${response.body()?.isSuccess}"
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
}
doUnmark(i, position)
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
if (debugReadingItems) {
ACRA.getErrorReporter().maybeHandleSilentException(t, app)
Toast.makeText(app.baseContext, t.message, Toast.LENGTH_LONG).show()
}
Toast.makeText(
app,
app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT
).show()
items.add(i)
notifyItemInserted(position)
updateItems(items)
thread {
db.itemsDao().insertAllItems(i.toEntity())
}
}
})
} }
fun addItemAtIndex(item: Item, position: Int) { fun addItemAtIndex(item: Item, position: Int) {

View File

@ -13,6 +13,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Source import apps.amine.bou.readerforselfoss.api.selfoss.Source
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator import com.amulyakhare.textdrawable.util.ColorGenerator
@ -70,33 +71,35 @@ class SourcesListAdapter(
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn) val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
deleteBtn.setOnClickListener { deleteBtn.setOnClickListener {
val (id) = items[adapterPosition] if (c.isNetworkAccessible(null)) {
api.deleteSource(id).enqueue(object : Callback<SuccessResponse> { val (id) = items[adapterPosition]
override fun onResponse( api.deleteSource(id).enqueue(object : Callback<SuccessResponse> {
call: Call<SuccessResponse>, override fun onResponse(
response: Response<SuccessResponse> call: Call<SuccessResponse>,
) { response: Response<SuccessResponse>
if (response.body() != null && response.body()!!.isSuccess) { ) {
items.removeAt(adapterPosition) if (response.body() != null && response.body()!!.isSuccess) {
notifyItemRemoved(adapterPosition) items.removeAt(adapterPosition)
notifyItemRangeChanged(adapterPosition, itemCount) notifyItemRemoved(adapterPosition)
} else { notifyItemRangeChanged(adapterPosition, itemCount)
} else {
Toast.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT
).show()
}
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
Toast.makeText( Toast.makeText(
app, app,
R.string.can_delete_source, R.string.can_delete_source,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} })
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
Toast.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT
).show()
}
})
} }
} }
} }

View File

@ -29,6 +29,7 @@ import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.openItemUrl import apps.amine.bou.readerforselfoss.utils.openItemUrl
import apps.amine.bou.readerforselfoss.utils.shareLink import apps.amine.bou.readerforselfoss.utils.shareLink
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
@ -135,35 +136,37 @@ class ArticleFragment : Fragment() {
false, false,
activity!! activity!!
) )
R.id.unread_action -> api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue( R.id.unread_action -> if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
object : Callback<SuccessResponse> { api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
override fun onResponse( object : Callback<SuccessResponse> {
call: Call<SuccessResponse>, override fun onResponse(
response: Response<SuccessResponse> call: Call<SuccessResponse>,
) { response: Response<SuccessResponse>
if (!response.succeeded() && debugReadingItems) { ) {
val message = if (!response.succeeded() && debugReadingItems) {
"message: ${response.message()} " + val message =
"response isSuccess: ${response.isSuccessful} " + "message: ${response.message()} " +
"response code: ${response.code()} " + "response isSuccess: ${response.isSuccessful} " +
"response message: ${response.message()} " + "response code: ${response.code()} " +
"response errorBody: ${response.errorBody()?.string()} " + "response message: ${response.message()} " +
"body success: ${response.body()?.success} " + "response errorBody: ${response.errorBody()?.string()} " +
"body isSuccess: ${response.body()?.isSuccess}" "body success: ${response.body()?.success} " +
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), activity!!) "body isSuccess: ${response.body()?.isSuccess}"
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), activity!!)
}
} }
}
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
if (debugReadingItems) { if (debugReadingItems) {
ACRA.getErrorReporter().maybeHandleSilentException(t, activity!!) ACRA.getErrorReporter().maybeHandleSilentException(t, activity!!)
}
} }
} }
} )
) }
else -> Unit else -> Unit
} }
} }
@ -212,96 +215,98 @@ class ArticleFragment : Fragment() {
customTabsIntent: CustomTabsIntent, customTabsIntent: CustomTabsIntent,
prefs: SharedPreferences prefs: SharedPreferences
) { ) {
rootView.progressBar.visibility = View.VISIBLE if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
val parser = MercuryApi( rootView.progressBar.visibility = View.VISIBLE
prefs.getBoolean("should_log_everything", false) val parser = MercuryApi(
) prefs.getBoolean("should_log_everything", false)
)
parser.parseUrl(url).enqueue( parser.parseUrl(url).enqueue(
object : Callback<ParsedContent> { object : Callback<ParsedContent> {
override fun onResponse( override fun onResponse(
call: Call<ParsedContent>, call: Call<ParsedContent>,
response: Response<ParsedContent> response: Response<ParsedContent>
) { ) {
// TODO: clean all the following after finding the mercury content issue // TODO: clean all the following after finding the mercury content issue
try { try {
if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) { if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
try {
rootView.titleView.text = response.body()!!.title
try { try {
// Note: Mercury may return relative urls... If it does the url val will not be changed. rootView.titleView.text = response.body()!!.title
URL(response.body()!!.url)
url = response.body()!!.url
} catch (e: MalformedURLException) {
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
}
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
try {
htmlToWebview(response.body()!!.content.orEmpty(), prefs)
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
try {
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
rootView.imageView.visibility = View.VISIBLE
try { try {
Glide // Note: Mercury may return relative urls... If it does the url val will not be changed.
.with(context!!) URL(response.body()!!.url)
.asBitmap() url = response.body()!!.url
.load(response.body()!!.lead_image_url) } catch (e: MalformedURLException) {
.apply(RequestOptions.fitCenterTransform()) ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
.into(rootView.imageView) }
} catch (e: Exception) { } catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!) ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
} }
} else {
rootView.imageView.visibility = View.GONE
} }
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
try { try {
rootView.nestedScrollView.scrollTo(0, 0) htmlToWebview(response.body()!!.content.orEmpty(), prefs)
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
rootView.progressBar.visibility = View.GONE try {
} catch (e: Exception) { if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
if (context != null) { rootView.imageView.visibility = View.VISIBLE
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!) try {
Glide
.with(context!!)
.asBitmap()
.load(response.body()!!.lead_image_url)
.apply(RequestOptions.fitCenterTransform())
.into(rootView.imageView)
} catch (e: Exception) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
} else {
rootView.imageView.visibility = View.GONE
}
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
try {
rootView.nestedScrollView.scrollTo(0, 0)
rootView.progressBar.visibility = View.GONE
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
} else {
try {
openInBrowserAfterFailing(customTabsIntent)
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
} }
} }
} else { } catch (e: Exception) {
try { if (context != null) {
openInBrowserAfterFailing(customTabsIntent) ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
} }
} }
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
} }
}
override fun onFailure( override fun onFailure(
call: Call<ParsedContent>, call: Call<ParsedContent>,
t: Throwable t: Throwable
) = openInBrowserAfterFailing(customTabsIntent) ) = openInBrowserAfterFailing(customTabsIntent)
} }
) )
}
} }
private fun htmlToWebview(c: String, prefs: SharedPreferences) { private fun htmlToWebview(c: String, prefs: SharedPreferences) {

View File

@ -10,13 +10,15 @@ import apps.amine.bou.readerforselfoss.R
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
var snackBarShown = false var snackBarShown = false
var view: View? = null
fun Context.isNetworkAccessible(v: View?): Boolean { fun Context.isNetworkAccessible(v: View?): Boolean {
val cm = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val cm = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork: NetworkInfo? = cm.activeNetworkInfo val activeNetwork: NetworkInfo? = cm.activeNetworkInfo
val networkIsAccessible = activeNetwork != null && activeNetwork.isConnectedOrConnecting val networkIsAccessible = activeNetwork != null && activeNetwork.isConnectedOrConnecting
if (v != null && !networkIsAccessible && !snackBarShown) { if (v != null && !networkIsAccessible && (!snackBarShown || v != view)) {
view = v
val s = Snackbar val s = Snackbar
.make( .make(
v, v,

View File

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:id="@+id/reader_activity_view"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout