From ef8005a46d036c86ef0ec112e5bc9d7051b0d529 Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Sun, 27 Dec 2020 17:16:06 +0400 Subject: [PATCH] Add a fallback url for when main urls don't work --- .../pageselect/PageSelectPresenter.kt | 4 ++ .../TranslationManagerPresenter.java | 51 ++++++++++--------- .../service/QuranDownloadService.java | 14 ++--- .../labs/androidquran/util/QuranFileUtils.kt | 6 ++- .../quran/labs/androidquran/util/UrlUtil.kt | 13 +++++ .../TranslationManagerPresenterTest.java | 3 +- 6 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/com/quran/labs/androidquran/util/UrlUtil.kt diff --git a/app/src/main/java/com/quran/labs/androidquran/pageselect/PageSelectPresenter.kt b/app/src/main/java/com/quran/labs/androidquran/pageselect/PageSelectPresenter.kt index f42b2c8531..9df918baf8 100644 --- a/app/src/main/java/com/quran/labs/androidquran/pageselect/PageSelectPresenter.kt +++ b/app/src/main/java/com/quran/labs/androidquran/pageselect/PageSelectPresenter.kt @@ -4,6 +4,7 @@ import com.quran.data.source.PageProvider import com.quran.labs.androidquran.presenter.Presenter import com.quran.labs.androidquran.util.ImageUtil import com.quran.labs.androidquran.util.QuranFileUtils +import com.quran.labs.androidquran.util.UrlUtil import dagger.Reusable import io.reactivex.Scheduler import io.reactivex.disposables.CompositeDisposable @@ -17,6 +18,7 @@ class PageSelectPresenter @Inject constructor(private val imageUtil: ImageUtil, private val quranFileUtils: QuranFileUtils, private val mainThreadScheduler: Scheduler, + private val urlUtil: UrlUtil, private val pageTypes: Map<@JvmSuppressWildcards String, @JvmSuppressWildcards PageProvider>) : Presenter { @@ -44,6 +46,8 @@ class PageSelectPresenter @Inject val url = "$baseUrl/${it.key}.png" compositeDisposable.add( imageUtil.downloadImage(url, previewImage) + .onErrorResumeNext( + imageUtil.downloadImage(urlUtil.fallbackUrl(url), previewImage)) .subscribeOn(Schedulers.io()) .observeOn(mainThreadScheduler) .subscribe({ generateData() }, { e -> Timber.e(e) }) diff --git a/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java b/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java index 3b78f925fd..7afe9b32c0 100644 --- a/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java +++ b/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java @@ -3,7 +3,8 @@ import android.content.Context; import android.util.Pair; import android.util.SparseArray; - +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.quran.labs.androidquran.common.LocalTranslation; import com.quran.labs.androidquran.dao.translation.Translation; import com.quran.labs.androidquran.dao.translation.TranslationItem; @@ -15,24 +16,20 @@ import com.quran.labs.androidquran.ui.TranslationManagerActivity; import com.quran.labs.androidquran.util.QuranFileUtils; import com.quran.labs.androidquran.util.QuranSettings; +import com.quran.labs.androidquran.util.UrlUtil; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; - +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.observers.DisposableObserver; +import io.reactivex.schedulers.Schedulers; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; - import javax.inject.Inject; import javax.inject.Singleton; - -import androidx.annotation.VisibleForTesting; -import androidx.annotation.WorkerThread; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.observers.DisposableObserver; -import io.reactivex.schedulers.Schedulers; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -51,6 +48,7 @@ public class TranslationManagerPresenter implements Presenter>() { @Override - public void onNext(List translationItems) { + public void onNext(@NonNull List translationItems) { if (currentActivity != null) { currentActivity.onTranslationsUpdated(translationItems); } @@ -152,13 +152,6 @@ public void updateItemOrdering(final List items) { .subscribe(); } - @WorkerThread - public Observable> syncTranslationsWithCache() { - return getCachedTranslationListObservable() - .filter(translationList -> !translationList.getTranslations().isEmpty()) - .map(translationList -> mergeWithServerTranslations(translationList.getTranslations())); - } - Observable getCachedTranslationListObservable() { return Observable.defer(() -> { try { @@ -179,9 +172,22 @@ Observable getCachedTranslationListObservable() { } Observable getRemoteTranslationListObservable() { + final String url = host + WEB_SERVICE_ENDPOINT; + return + downloadTranslationList(url) + .onErrorResumeNext(downloadTranslationList(urlUtil.fallbackUrl(url))) + .doOnNext(translationList -> { + translationList.getTranslations(); + if (!translationList.getTranslations().isEmpty()) { + writeTranslationList(translationList); + } + }); + } + + private Observable downloadTranslationList(String url) { return Observable.fromCallable(() -> { Request request = new Request.Builder() - .url(host + WEB_SERVICE_ENDPOINT) + .url(url) .build(); Response response = okHttpClient.newCall(request).execute(); @@ -192,11 +198,6 @@ Observable getRemoteTranslationListObservable() { TranslationList result = jsonAdapter.fromJson(responseBody.source()); responseBody.close(); return result; - }).doOnNext(translationList -> { - translationList.getTranslations(); - if (!translationList.getTranslations().isEmpty()) { - writeTranslationList(translationList); - } }); } diff --git a/app/src/main/java/com/quran/labs/androidquran/service/QuranDownloadService.java b/app/src/main/java/com/quran/labs/androidquran/service/QuranDownloadService.java index c8a2a572d1..d07b89c738 100644 --- a/app/src/main/java/com/quran/labs/androidquran/service/QuranDownloadService.java +++ b/app/src/main/java/com/quran/labs/androidquran/service/QuranDownloadService.java @@ -22,6 +22,7 @@ import com.quran.labs.androidquran.service.util.QuranDownloadNotifier.ProgressIntent; import com.quran.labs.androidquran.util.QuranSettings; import com.quran.labs.androidquran.util.QuranUtils; +import com.quran.labs.androidquran.util.UrlUtil; import com.quran.labs.androidquran.util.ZipUtils; import java.io.File; @@ -98,6 +99,8 @@ public class QuranDownloadService extends Service implements private ServiceHandler serviceHandler; private QuranDownloadNotifier notifier; + private static boolean fallbackByDefault = false; + // written from ui thread and read by download thread private volatile boolean isDownloadCanceled; private LocalBroadcastManager broadcastManager; @@ -109,10 +112,11 @@ public class QuranDownloadService extends Service implements private Map recentlyFailedDownloads = null; // incremented from ui thread and decremented by download thread - private AtomicInteger currentOperations = new AtomicInteger(0); + private final AtomicInteger currentOperations = new AtomicInteger(0); @Inject QuranInfo quranInfo; @Inject OkHttpClient okHttpClient; + @Inject UrlUtil urlUtil; private final class ServiceHandler extends Handler { @@ -464,11 +468,8 @@ private boolean downloadFileWrapper(String urlString, String destination, } String url = urlString; - if (i > 0) { - // let's try http instead of https? - if (urlString.contains("quran.com") || urlString.contains("quranicaudio.com")) { - url = urlString.replace("https://", "http://"); - } + if (fallbackByDefault || i > 0) { + url = urlUtil.fallbackUrl(url); // want to wait before retrying again try { @@ -486,6 +487,7 @@ private boolean downloadFileWrapper(String urlString, String destination, } if (res == DOWNLOAD_SUCCESS) { + fallbackByDefault = (i > 0); return true; } else if (res == QuranDownloadNotifier.ERROR_DISK_SPACE || res == QuranDownloadNotifier.ERROR_PERMISSIONS) { diff --git a/app/src/main/java/com/quran/labs/androidquran/util/QuranFileUtils.kt b/app/src/main/java/com/quran/labs/androidquran/util/QuranFileUtils.kt index 3e3f5d4a5f..d538e217d6 100644 --- a/app/src/main/java/com/quran/labs/androidquran/util/QuranFileUtils.kt +++ b/app/src/main/java/com/quran/labs/androidquran/util/QuranFileUtils.kt @@ -37,7 +37,8 @@ import javax.inject.Inject class QuranFileUtils @Inject constructor( context: Context, private val pageProvider: PageProvider, - private val quranScreenInfo: QuranScreenInfo + private val quranScreenInfo: QuranScreenInfo, + private val urlUtil: UrlUtil ) { // server urls private val imageBaseUrl: String = pageProvider.getImagesBaseUrl() @@ -267,7 +268,8 @@ class QuranFileUtils @Inject constructor( filename: String, isRetry: Boolean ): Response { - val urlString = (imageBaseUrl + "width" + widthParam + File.separator + filename) + val base = if (isRetry) urlUtil.fallbackUrl(imageBaseUrl) else imageBaseUrl + val urlString = (base + "width" + widthParam + File.separator + filename) Timber.d("want to download: %s", urlString) val request = Builder() .url(urlString) diff --git a/app/src/main/java/com/quran/labs/androidquran/util/UrlUtil.kt b/app/src/main/java/com/quran/labs/androidquran/util/UrlUtil.kt new file mode 100644 index 0000000000..e933414b91 --- /dev/null +++ b/app/src/main/java/com/quran/labs/androidquran/util/UrlUtil.kt @@ -0,0 +1,13 @@ +package com.quran.labs.androidquran.util + +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class UrlUtil @Inject constructor() { + + fun fallbackUrl(url: String): String { + return url.replace("android.quran.com", "quran.app") + .replace(".quranicaudio.com", ".quranicaudio.org") + } +} diff --git a/app/src/test/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenterTest.java b/app/src/test/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenterTest.java index 978ce0c8c0..5fde68f50d 100644 --- a/app/src/test/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenterTest.java +++ b/app/src/test/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenterTest.java @@ -6,6 +6,7 @@ import com.quran.labs.androidquran.util.QuranFileUtils; import com.quran.labs.androidquran.util.QuranSettings; +import com.quran.labs.androidquran.util.UrlUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -37,7 +38,7 @@ public void setup() { mockWebServer = new MockWebServer(); translationManager = new TranslationManagerPresenter( mockAppContext, mockOkHttp, mockSettings, null, - mock(QuranFileUtils.class)) { + mock(QuranFileUtils.class), new UrlUtil()) { @Override void writeTranslationList(TranslationList list) { // no op