From 149ecaa5927579f220f4a46b1ec5f0e1e05b4f34 Mon Sep 17 00:00:00 2001 From: Carlos <2092019+CarlosEsco@users.noreply.github.com> Date: Wed, 6 May 2020 20:21:14 -0400 Subject: [PATCH] Wrap stuff in manga details with an online check to improve user experience (#344) * wrap details stuff in network checks so app doesn't reach out when it knows it cant * wrap some more clean up a little * remove online check for chapter click/resume in mangadetail * code review fixes --- .../ui/manga/MangaDetailsController.kt | 30 ++++++++--- .../ui/manga/MangaDetailsPresenter.kt | 53 ++++++++++--------- .../ui/manga/track/TrackingBottomSheet.kt | 25 ++++++++- .../util/system/ContextExtensions.kt | 20 +++++++ 4 files changed, 94 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index a1834f8a1a..14e548af35 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -97,6 +97,7 @@ import eu.kanade.tachiyomi.util.system.ThemeUtil import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.isInNightMode +import eu.kanade.tachiyomi.util.system.isOnline import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.getText @@ -515,6 +516,14 @@ class MangaDetailsController : BaseController, } //endregion + fun isNotOnline(showSnackbar: Boolean = true): Boolean { + if (activity == null || !activity!!.isOnline()) { + if (showSnackbar) view?.snack(R.string.no_network_connection) + return true + } + return false + } + fun showError(message: String) { swipe_refresh?.isRefreshing = presenter.isLoading view?.snack(message) @@ -599,7 +608,8 @@ class MangaDetailsController : BaseController, fun refreshAdapter() = adapter?.notifyDataSetChanged() override fun onItemClick(view: View?, position: Int): Boolean { - val chapter = (adapter?.getItem(position) as? ChapterItem)?.chapter ?: return false + val chapterItem = (adapter?.getItem(position) as? ChapterItem) ?: return false + val chapter = chapterItem.chapter if (actionMode != null) { if (startingDLChapterPos == null) { adapter?.addSelection(position) @@ -629,6 +639,7 @@ class MangaDetailsController : BaseController, return false } openChapter(chapter) + return false } @@ -828,13 +839,15 @@ class MangaDetailsController : BaseController, } } R.id.action_open_in_web_view -> openInWebView() - R.id.action_refresh_tracking -> presenter.refreshTrackers() + R.id.action_refresh_tracking -> presenter.refreshTrackers(true) R.id.action_migrate -> - PreMigrationController.navigateToMigration( - presenter.preferences.skipPreMigration().getOrDefault(), - router, - listOf(manga!!.id!!) - ) + if (!isNotOnline()) { + PreMigrationController.navigateToMigration( + presenter.preferences.skipPreMigration().getOrDefault(), + router, + listOf(manga!!.id!!) + ) + } R.id.action_mark_all_as_read -> { MaterialDialog(view!!.context).message(R.string.mark_all_chapters_as_read) .positiveButton(R.string.mark_as_read) { @@ -892,8 +905,8 @@ class MangaDetailsController : BaseController, } override fun openInWebView() { + if (isNotOnline()) return val source = presenter.source as? HttpSource ?: return - val url = try { source.mangaDetailsRequest(presenter.manga).url.toString() } catch (e: Exception) { @@ -1053,6 +1066,7 @@ class MangaDetailsController : BaseController, } override fun globalSearch(text: String) { + if (isNotOnline()) return router.pushController(SourceSearchController(text).withFadeTransaction()) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index 15c309e239..e609238f3c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -93,7 +93,7 @@ class MangaDetailsPresenter( controller.updateChapters(this.chapters) } fetchTrackings() - refreshTrackers() + refreshTrackers(false) } fun onDestroy() { @@ -372,6 +372,7 @@ class MangaDetailsPresenter( /** Refresh Manga Info and Chapter List (not tracking) */ fun refreshAll() { + if (controller.isNotOnline()) return scope.launch { isLoading = true var mangaError: java.lang.Exception? = null @@ -763,36 +764,40 @@ class MangaDetailsPresenter( withContext(Dispatchers.Main) { controller.refreshTracking(trackList) } } - fun refreshTrackers() { - scope.launch { - trackList.filter { it.track != null }.map { item -> - withContext(Dispatchers.IO) { - val trackItem = try { - item.service.refresh(item.track!!) - } catch (e: Exception) { - trackError(e) - null + fun refreshTrackers(showOfflineSnack: Boolean = false) { + if (controller.isNotOnline(showOfflineSnack)) { + scope.launch { + trackList.filter { it.track != null }.map { item -> + withContext(Dispatchers.IO) { + val trackItem = try { + item.service.refresh(item.track!!) + } catch (e: Exception) { + trackError(e) + null + } + if (trackItem != null) { + db.insertTrack(trackItem).executeAsBlocking() + trackItem + } else item.track } - if (trackItem != null) { - db.insertTrack(trackItem).executeAsBlocking() - trackItem - } else item.track } + refreshTracking() } - refreshTracking() } } fun trackSearch(query: String, service: TrackService) { - scope.launch(Dispatchers.IO) { - val results = try { - service.search(query) - } catch (e: Exception) { - withContext(Dispatchers.Main) { controller.trackSearchError(e) } - null - } - if (!results.isNullOrEmpty()) { - withContext(Dispatchers.Main) { controller.onTrackSearchResults(results) } + if (controller.isNotOnline()) { + scope.launch(Dispatchers.IO) { + val results = try { + service.search(query) + } catch (e: Exception) { + withContext(Dispatchers.Main) { controller.trackSearchError(e) } + null + } + if (!results.isNullOrEmpty()) { + withContext(Dispatchers.Main) { controller.onTrackSearchResults(results) } + } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt index 2302f24009..be48d152ac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener +import eu.kanade.tachiyomi.util.view.hide import eu.kanade.tachiyomi.util.view.setEdgeToEdge import kotlinx.android.synthetic.main.tracking_bottom_sheet.* import timber.log.Timber @@ -106,8 +107,12 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott activity.toast(error.message) } - override fun onLogoClick(position: Int) { + override fun onLogoClick(position: Int) { val track = adapter?.getItem(position)?.track ?: return + if (controller.isNotOnline()) { + sheetBehavior.hide() + return + } if (track.tracking_url.isBlank()) { activity.toast(R.string.url_not_set_click_again) @@ -119,6 +124,11 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott override fun onSetClick(position: Int) { val item = adapter?.getItem(position) ?: return + if (controller.isNotOnline()) { + sheetBehavior.hide() + return + } + TrackSearchDialog(this, item.service, item.track != null).showDialog( controller.router, TAG_SEARCH_CONTROLLER @@ -128,6 +138,10 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott override fun onStatusClick(position: Int) { val item = adapter?.getItem(position) ?: return if (item.track == null) return + if (controller.isNotOnline()) { + dismiss() + return + } SetTrackStatusDialog(this, item).showDialog(controller.router) } @@ -135,13 +149,20 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott override fun onChaptersClick(position: Int) { val item = adapter?.getItem(position) ?: return if (item.track == null) return - + if (controller.isNotOnline()) { + dismiss() + return + } SetTrackChaptersDialog(this, item).showDialog(controller.router) } override fun onScoreClick(position: Int) { val item = adapter?.getItem(position) ?: return if (item.track == null) return + if (controller.isNotOnline()) { + dismiss() + return + } SetTrackScoreDialog(this, item).showDialog(controller.router) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 5f953b51cd..b0889cec8b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -12,6 +12,7 @@ import android.content.res.Configuration import android.content.res.Resources import android.graphics.drawable.Drawable import android.net.ConnectivityManager +import android.net.NetworkCapabilities import android.net.Uri import android.os.PowerManager import android.view.View @@ -219,3 +220,22 @@ fun Context.isInNightMode(): Boolean { val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK return currentNightMode == Configuration.UI_MODE_NIGHT_YES } + +fun Context.isOnline(): Boolean { + val connectivityManager = this + .getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager + var result = false + connectivityManager?.let { + val networkCapabilities = connectivityManager.activeNetwork ?: return false + val actNw = + connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false + result = when { + actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true + actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true + actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true + else -> false + } + } + + return result +}