diff --git a/app/build.gradle b/app/build.gradle index ed4cb250..0ae3584e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { targetSdkVersion 33 // I don't know why I've used such a weird versioning scheme in the beginning, // but I can't change it now as the app is already in the Play Store - versionCode 23_04_02_001 - versionName "2023.04.02.1" + versionCode 23_04_21_001 + versionName "2023.04.21.1" resValue "string", "app_name", "QuranApp" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d2db1bc7..87b9252c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,27 +38,41 @@ - + + + + + + + android:exported="true" + android:launchMode="singleTask"> + + + + + + + + + + - - - + + + + + + + - + + + + + + + + + pathSegments = data.getPathSegments(); - chapterNo = Integer.parseInt(pathSegments.get(1)); - - String lang = data.getQueryParameter("language"); - if (lang == null) { - lang = data.getQueryParameter("lang"); - } - mLanguage = lang; - } catch (Exception ignored) { + chapterNo = validateIntent(intent); + } catch (Exception e) { + e.printStackTrace(); invalidParams(); + return; } } else { chapterNo = intent.getIntExtra(Keys.READER_KEY_CHAPTER_NO, DEFAULT_CHAPTER_INFO); @@ -128,6 +128,27 @@ private void initContent(Intent intent) { loadContent(); } + private int validateIntent(Intent intent) { + Uri url = intent.getData(); + if (INTENT_ACTION_OPEN_CHAPTER_INFO.equalsIgnoreCase(intent.getAction())) { + mLanguage = intent.getStringExtra("language"); + return intent.getIntExtra("chapterNo", -1); + } else if (url.getHost().equalsIgnoreCase("quran.com")) { + List pathSegments = url.getPathSegments(); + String lang = url.getQueryParameter("language"); + + if (lang == null) { + lang = url.getQueryParameter("lang"); + } + + mLanguage = lang; + + return Integer.parseInt(pathSegments.get(1)); + } else { + throw new IllegalArgumentException("Invalid params"); + } + } + private void initThis() { mBinding.title.setText(R.string.strTitleAboutSurah); mBinding.back.setOnClickListener(v -> finish()); diff --git a/app/src/main/java/com/quranapp/android/activities/ActivityReader.java b/app/src/main/java/com/quranapp/android/activities/ActivityReader.java index f25e94dd..b69f9b18 100644 --- a/app/src/main/java/com/quranapp/android/activities/ActivityReader.java +++ b/app/src/main/java/com/quranapp/android/activities/ActivityReader.java @@ -1,34 +1,5 @@ package com.quranapp.android.activities; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; -import static com.quranapp.android.components.quran.QuranMeta.canShowBismillah; -import static com.quranapp.android.reader_managers.ReaderParams.READER_READ_TYPE_CHAPTER; -import static com.quranapp.android.reader_managers.ReaderParams.READER_READ_TYPE_JUZ; -import static com.quranapp.android.reader_managers.ReaderParams.READER_READ_TYPE_VERSES; -import static com.quranapp.android.reader_managers.ReaderParams.READER_STYLE_PAGE; -import static com.quranapp.android.reader_managers.ReaderParams.READER_STYLE_TRANSLATION; -import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.BISMILLAH; -import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.CHAPTER_TITLE; -import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.IS_VOTD; -import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.NO_TRANSL_SELECTED; -import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.READER_PAGE; -import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.VERSE; -import static com.quranapp.android.utils.quran.QuranUtils.doesVerseRangeEqualWhole; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_CHAPTER_NO; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_JUZ_NO; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_PENDING_SCROLL; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_READER_STYLE; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_READ_TYPE; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_SAVE_TRANSL_CHANGES; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_TRANSL_SLUGS; -import static com.quranapp.android.utils.univ.Keys.READER_KEY_VERSES; -import static com.quranapp.android.utils.univ.RegexPattern.VERSE_RANGE_PATTERN; - import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; @@ -46,7 +17,6 @@ import android.view.Window; import android.widget.LinearLayout; import android.widget.Toast; - import androidx.activity.result.ActivityResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -55,6 +25,34 @@ import androidx.core.view.WindowInsetsControllerCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import static com.quranapp.android.components.quran.QuranMeta.canShowBismillah; +import static com.quranapp.android.reader_managers.ReaderParams.READER_READ_TYPE_CHAPTER; +import static com.quranapp.android.reader_managers.ReaderParams.READER_READ_TYPE_JUZ; +import static com.quranapp.android.reader_managers.ReaderParams.READER_READ_TYPE_VERSES; +import static com.quranapp.android.reader_managers.ReaderParams.READER_STYLE_PAGE; +import static com.quranapp.android.reader_managers.ReaderParams.READER_STYLE_TRANSLATION; +import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.BISMILLAH; +import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.CHAPTER_TITLE; +import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.IS_VOTD; +import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.NO_TRANSL_SELECTED; +import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.READER_PAGE; +import static com.quranapp.android.reader_managers.ReaderParams.RecyclerItemViewType.VERSE; +import static com.quranapp.android.utils.IntentUtils.INTENT_ACTION_OPEN_READER; +import static com.quranapp.android.utils.quran.QuranUtils.doesVerseRangeEqualWhole; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_CHAPTER_NO; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_JUZ_NO; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_PENDING_SCROLL; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_READER_STYLE; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_READ_TYPE; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_SAVE_TRANSL_CHANGES; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_TRANSL_SLUGS; +import static com.quranapp.android.utils.univ.Keys.READER_KEY_VERSES; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import com.quranapp.android.R; import com.quranapp.android.adapters.ADPQuranPages; @@ -74,7 +72,6 @@ import com.quranapp.android.reader_managers.Navigator; import com.quranapp.android.reader_managers.ReaderParams; import com.quranapp.android.suppliments.ReaderLayoutManager; -import com.quranapp.android.utils.Log; import com.quranapp.android.utils.quran.QuranUtils; import com.quranapp.android.utils.reader.factory.ReaderFactory; import com.quranapp.android.utils.reader.recitation.RecitationUtils; @@ -100,8 +97,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; import java.util.stream.IntStream; import kotlin.Pair; @@ -139,15 +134,25 @@ public void onServiceConnected(ComponentName name, IBinder service) { QuranMeta quranMeta = mQuranMetaRef.get(); if (mReaderParams.readType == READER_READ_TYPE_JUZ && currJuzNo > 0 && quranMeta != null) { - mPlayerService.onJuzChanged( - currJuzNo, - quranMeta - ); + mPlayerService.onJuzChanged(currJuzNo, quranMeta); } else if (currChapter != null) { + final int fromVerse; + final int toVerse; + Pair verseRange = mReaderParams.verseRange; + + if (QuranUtils.doesRangeDenoteSingle(verseRange)) { + fromVerse = 1; + toVerse = currChapter.getVerseCount(); + } else { + fromVerse = verseRange.getFirst(); + toVerse = verseRange.getSecond(); + } + mPlayerService.onChapterChanged( currChapter.getChapterNumber(), - 1, - currChapter.getVerseCount() + fromVerse, + toVerse, + mPlayerService.getP().getCurrentVerseNo() ); } } @@ -365,48 +370,87 @@ private void validateIntent(Intent intent) { if (Intent.ACTION_VIEW.equals(action)) { Uri url = intent.getData(); - List pathSegments = url.getPathSegments(); - if (pathSegments.size() >= 2) { - String firstSeg = pathSegments.get(0); - String secondSeg = pathSegments.get(1); - - if (firstSeg.equalsIgnoreCase("juz")) { - int juzNo = Integer.parseInt(secondSeg); - intent.putExtras(ReaderFactory.prepareJuzIntent(juzNo)); - } else if (firstSeg.equalsIgnoreCase("chapter") || firstSeg.equalsIgnoreCase("surah")) { - int chapterNo = Integer.parseInt(secondSeg); - intent.putExtras(ReaderFactory.prepareChapterIntent(chapterNo)); + if (url == null) return; + + if (url.getHost().equalsIgnoreCase("quran.com")) { + validateQuranComIntent(intent, url); + } + } else if (INTENT_ACTION_OPEN_READER.equalsIgnoreCase(intent.getAction())) { + validateQuranAppIntent(intent); + } + + intent.setAction(null); + } + + private void validateQuranComIntent(Intent intent, Uri url) { + List pathSegments = url.getPathSegments(); + if (pathSegments.size() >= 2) { + String firstSeg = pathSegments.get(0); + String secondSeg = pathSegments.get(1); + + if (firstSeg.equalsIgnoreCase("juz")) { + int juzNo = Integer.parseInt(secondSeg); + intent.putExtras(ReaderFactory.prepareJuzIntent(juzNo)); + } else { + int chapterNo = Integer.parseInt(firstSeg); + + final Pair verseRange; + final String[] splits = secondSeg.split("-"); + if (splits.length >= 2) { + verseRange = new Pair<>(Integer.parseInt(splits[0]), Integer.parseInt(splits[1])); } else { - int chapterNo = Integer.parseInt(firstSeg); - - final Pair verseRange; - Matcher matcher = VERSE_RANGE_PATTERN.matcher(secondSeg); - MatchResult result; - if (matcher.find() && (result = matcher.toMatchResult()).groupCount() >= 2) { - final int fromVerse = Integer.parseInt(result.group(1)); - final int toVerse = Integer.parseInt(result.group(2)); - - verseRange = new Pair<>(fromVerse, toVerse); - } else { - int verseNo = Integer.parseInt(secondSeg); - verseRange = new Pair<>(verseNo, verseNo); - } + int verseNo = Integer.parseInt(splits[0]); + verseRange = new Pair<>(verseNo, verseNo); + } - intent.putExtras(ReaderFactory.prepareVerseRangeIntent(chapterNo, verseRange)); + intent.putExtras(ReaderFactory.prepareVerseRangeIntent(chapterNo, verseRange)); + } + } else if (pathSegments.size() >= 1) { + String[] splits = pathSegments.get(0).split(":"); + int chapterNo = Integer.parseInt(splits[0]); + if (splits.length >= 2) { + splits = splits[1].split("-"); + final Pair verseRange; + if (splits.length >= 2) { + verseRange = new Pair<>(Integer.parseInt(splits[0]), Integer.parseInt(splits[1])); + } else { + int verseNo = Integer.parseInt(splits[0]); + verseRange = new Pair<>(verseNo, verseNo); } - } else if (pathSegments.size() >= 1) { - int chapterNo = Integer.parseInt(pathSegments.get(0)); + intent.putExtras(ReaderFactory.prepareVerseRangeIntent(chapterNo, verseRange)); + } else { intent.putExtras(ReaderFactory.prepareChapterIntent(chapterNo)); } + } - Set parameters = url.getQueryParameterNames(); - if (parameters.contains("reading")) { - boolean reading = url.getBooleanQueryParameter("reading", false); - mReaderParams.setReaderStyle(this, reading ? READER_STYLE_PAGE : READER_STYLE_TRANSLATION); - } + Set parameters = url.getQueryParameterNames(); + if (parameters.contains("reading")) { + boolean reading = url.getBooleanQueryParameter("reading", false); + mReaderParams.setReaderStyle(this, reading ? READER_STYLE_PAGE : READER_STYLE_TRANSLATION); } + } - intent.setAction(null); + private void validateQuranAppIntent(Intent intent) { + final String[] requestedTranslSlugs = intent.getStringArrayExtra("translations"); + if (requestedTranslSlugs != null) { + mReaderParams.setVisibleTranslSlugs(new TreeSet<>(Arrays.asList(requestedTranslSlugs))); + } + + if (intent.getBooleanExtra("isJuz", false)) { + final int juzNo = intent.getIntExtra("juzNo", -1); + intent.putExtras(ReaderFactory.prepareJuzIntent(juzNo)); + } else { + final int chapterNo = intent.getIntExtra("chapterNo", -1); + int[] verses = intent.getIntArrayExtra("verses"); + int verseNo = intent.getIntExtra("verseNo", -1); + if (verses != null) { + intent.putExtras(ReaderFactory.prepareVerseRangeIntent(chapterNo, verses[0], verses[1])); + } else if (verseNo != -1) { + intent.putExtras(ReaderFactory.prepareSingleVerseIntent(chapterNo, verseNo)); + } else { + intent.putExtras(ReaderFactory.prepareChapterIntent(chapterNo)); + } + } } private void initReader() { @@ -576,7 +620,13 @@ public void initChapter(Chapter chapter) { if (mPlayer != null) { if (!mProtectFromPlayerReset) { - mPlayer.onChapterChanged(chapter.getChapterNumber(), 1, chapter.getVerseCount(), false); + mPlayer.onChapterChanged( + chapter.getChapterNumber(), + 1, + chapter.getVerseCount(), + 1, + false + ); } else { mPlayer.reveal(); } @@ -615,9 +665,25 @@ public void initVerseRange(Chapter chapter, Pair verseRange) { mReaderParams.verseRange = verseRange; if (mPlayer != null) { - if (!mProtectFromPlayerReset && - (!chapter.equals(mReaderParams.currChapter))) { - mPlayer.onChapterChanged(chapter.getChapterNumber(), 1, chapter.getVerseCount(), false); + if (!mProtectFromPlayerReset && (!chapter.equals(mReaderParams.currChapter))) { + final int fromVerse; + final int toVerse; + + if (QuranUtils.doesRangeDenoteSingle(verseRange)) { + fromVerse = 1; + toVerse = chapter.getVerseCount(); + } else { + fromVerse = verseRange.getFirst(); + toVerse = verseRange.getSecond(); + } + + mPlayer.onChapterChanged( + chapter.getChapterNumber(), + fromVerse, + toVerse, + verseRange.getFirst(), + false + ); } else { mPlayer.reveal(); } diff --git a/app/src/main/java/com/quranapp/android/activities/ActivityReference.java b/app/src/main/java/com/quranapp/android/activities/ActivityReference.java index 43389482..ea2847df 100644 --- a/app/src/main/java/com/quranapp/android/activities/ActivityReference.java +++ b/app/src/main/java/com/quranapp/android/activities/ActivityReference.java @@ -14,6 +14,7 @@ import static com.quranapp.android.adapters.ADPReferenceVerses.VIEWTYPE_DESCRIPTION; import static com.quranapp.android.adapters.ADPReferenceVerses.VIEWTYPE_TITLE; import static com.quranapp.android.adapters.ADPReferenceVerses.VIEWTYPE_VERSE; +import static com.quranapp.android.utils.IntentUtils.INTENT_ACTION_OPEN_REFERENCE; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -31,7 +32,10 @@ import com.quranapp.android.components.quran.subcomponents.Verse; import com.quranapp.android.databinding.ActivityReferenceBinding; import com.quranapp.android.databinding.LytChipgroupBinding; +import com.quranapp.android.utils.Log; +import com.quranapp.android.utils.quran.parser.ParserUtils; import com.quranapp.android.utils.reader.TranslUtils; +import com.quranapp.android.utils.reader.factory.ReaderFactory; import com.quranapp.android.utils.sharedPrefs.SPReader; import com.quranapp.android.utils.thread.runner.RunnableTaskRunner; import com.quranapp.android.utils.thread.tasks.BaseRunnableTask; @@ -147,8 +151,18 @@ private void resetAppBar(float elevation, boolean titleShown) { } private void init(Intent intent) { - ReferenceVerseModel refModel = (ReferenceVerseModel) intent.getSerializableExtra( - Keys.KEY_REFERENCE_VERSE_MODEL); + ReferenceVerseModel refModel = null; + + try { + refModel = validateIntent(intent); + } catch (Exception e) { + e.printStackTrace(); + } + + if (refModel == null) { + refModel = (ReferenceVerseModel) intent.getSerializableExtra(Keys.KEY_REFERENCE_VERSE_MODEL); + } + if (refModel == null) { return; } @@ -171,14 +185,22 @@ private void init(Intent intent) { mSelectedTranslSlugs = TranslUtils.defaultTranslationSlugs(); } - /*QuranTransl.prepareInstance(this, mSelectedTranslSlugs, quranTransl -> { - setVisibleTranslSlugs(mSelectedTranslSlugs); - setQuranTransl(quranTransl, true); + initContent(refModel); + } - initContent(refModel); - });*/ + private ReferenceVerseModel validateIntent(Intent intent) { + if (!INTENT_ACTION_OPEN_REFERENCE.equalsIgnoreCase(intent.getAction())) { + return null; + } - initContent(refModel); + final String title = intent.getStringExtra("title"); + final String desc = intent.getStringExtra("description"); + final String[] translSlugs = intent.getStringArrayExtra("translations"); + final boolean showChapterSugg = intent.getBooleanExtra("showChapterSuggestions", true); + final List verses = ParserUtils.prepareVersesList(intent.getStringExtra("verses"), true); + final List chapters = ParserUtils.prepareChaptersList(verses); + + return new ReferenceVerseModel(showChapterSugg, title, desc, translSlugs, chapters, verses); } private void initContent(ReferenceVerseModel refModel) { diff --git a/app/src/main/java/com/quranapp/android/activities/ActivityTafsir.kt b/app/src/main/java/com/quranapp/android/activities/ActivityTafsir.kt index d5892ad0..f3f03911 100644 --- a/app/src/main/java/com/quranapp/android/activities/ActivityTafsir.kt +++ b/app/src/main/java/com/quranapp/android/activities/ActivityTafsir.kt @@ -196,7 +196,7 @@ class ActivityTafsir : ReaderPossessingActivity() { } private fun initContent(intent: Intent) { - var key = SPReader.getSavedTafsirKey(this) + var key = intent.getStringExtra("tafsirKey") ?: SPReader.getSavedTafsirKey(this) val chapterNo = intent.getIntExtra(Keys.READER_KEY_CHAPTER_NO, -1) val verseNo = intent.getIntExtra(Keys.READER_KEY_VERSE_NO, -1) diff --git a/app/src/main/java/com/quranapp/android/adapters/tafsir/ADPTafsir.kt b/app/src/main/java/com/quranapp/android/adapters/tafsir/ADPTafsir.kt index 5ae1d957..62676f9a 100644 --- a/app/src/main/java/com/quranapp/android/adapters/tafsir/ADPTafsir.kt +++ b/app/src/main/java/com/quranapp/android/adapters/tafsir/ADPTafsir.kt @@ -21,7 +21,7 @@ class ADPTafsir(private val tafsirs: List, private val selectCa checkBox.isChecked = info.isChecked checkBox.onCheckChangedListener = { _, _ -> SPReader.setSavedTafsirKey(checkBox.context, info.key) - selectCallback(adapterPosition) + selectCallback(bindingAdapterPosition) } } diff --git a/app/src/main/java/com/quranapp/android/api/ApiConfig.kt b/app/src/main/java/com/quranapp/android/api/ApiConfig.kt index 11dc3900..5e462c42 100644 --- a/app/src/main/java/com/quranapp/android/api/ApiConfig.kt +++ b/app/src/main/java/com/quranapp/android/api/ApiConfig.kt @@ -7,7 +7,7 @@ package com.quranapp.android.api object ApiConfig { - const val GITHUB_ROOT_URL = "https://raw.githubusercontent.com/AlfaazPlus/QuranApp/master/" + const val GITHUB_ROOT_URL = "https://cdn.jsdelivr.net/gh/AlfaazPlus/QuranApp/" const val GITHUB_REPOSITORY_URL = "https://github.com/AlfaazPlus/QuranApp" const val GITHUB_ISSUES_BUG_REPORT_URL = "https://github.com/AlfaazPlus/QuranApp/issues/new?template=bug_report.yml" const val GITHUB_ISSUES_VERSE_REPORT_URL = "https://github.com/AlfaazPlus/QuranApp/issues/new?template=verse_report.yml" diff --git a/app/src/main/java/com/quranapp/android/api/GithubApi.kt b/app/src/main/java/com/quranapp/android/api/GithubApi.kt index af0c5021..f49bfac0 100644 --- a/app/src/main/java/com/quranapp/android/api/GithubApi.kt +++ b/app/src/main/java/com/quranapp/android/api/GithubApi.kt @@ -4,8 +4,11 @@ import com.quranapp.android.api.models.AppUpdate import com.quranapp.android.api.models.AppUrls import com.quranapp.android.api.models.ResourcesVersions import okhttp3.ResponseBody +import retrofit2.Response import retrofit2.http.GET +import retrofit2.http.Headers import retrofit2.http.Path +import retrofit2.http.Streaming interface GithubApi { @GET("inventory/versions/app_updates.json") @@ -21,16 +24,19 @@ interface GithubApi { suspend fun getAvailableTranslations(): ResponseBody @GET("{path}") - suspend fun getTranslation(@Path("path") path: String): ResponseBody + @Streaming + suspend fun getTranslation(@Path("path") path: String): Response @GET("inventory/quran_scripts/{filename}") - suspend fun getQuranScript(@Path("filename") filename: String): ResponseBody + @Streaming + suspend fun getQuranScript(@Path("filename") filename: String): Response @GET("inventory/fonts/{scriptKey}/{part}") + @Streaming suspend fun getKFQPCFont( @Path("scriptKey") scriptKey: String, @Path("part") part: String - ): ResponseBody + ): Response @GET("inventory/recitations/available_recitations_info.json") suspend fun getAvailableRecitations(): ResponseBody diff --git a/app/src/main/java/com/quranapp/android/utils/IntentUtils.kt b/app/src/main/java/com/quranapp/android/utils/IntentUtils.kt index a480559f..bd3e5a27 100644 --- a/app/src/main/java/com/quranapp/android/utils/IntentUtils.kt +++ b/app/src/main/java/com/quranapp/android/utils/IntentUtils.kt @@ -1,5 +1,8 @@ package com.quranapp.android.utils object IntentUtils { - const val INTENT_ACTION_APP_LANGUAGE_CHANGED = "intent.action.app_language_changed" + const val INTENT_ACTION_OPEN_READER = "com.quranapp.android.action.OPEN_READER" + const val INTENT_ACTION_OPEN_REFERENCE = "com.quranapp.android.action.OPEN_REFERENCE" + const val INTENT_ACTION_OPEN_CHAPTER_INFO = "com.quranapp.android.action.OPEN_CHAPTER_INFO" + const val INTENT_ACTION_OPEN_TAFSIR = "com.quranapp.android.action.OPEN_TAFSIR" } diff --git a/app/src/main/java/com/quranapp/android/utils/Log.kt b/app/src/main/java/com/quranapp/android/utils/Log.kt index f1a3339e..932f635e 100644 --- a/app/src/main/java/com/quranapp/android/utils/Log.kt +++ b/app/src/main/java/com/quranapp/android/utils/Log.kt @@ -26,6 +26,7 @@ object Log { return file.readText() } + @JvmStatic fun saveCrash(ctx: Context, e: Throwable?) { if (e == null) return @@ -49,6 +50,7 @@ object Log { } + @JvmStatic fun saveError(e: Throwable?, place: String) { if (e == null) return diff --git a/app/src/main/java/com/quranapp/android/utils/extensions/RetrofitResponse.kt b/app/src/main/java/com/quranapp/android/utils/extensions/RetrofitResponse.kt new file mode 100644 index 00000000..3dc92845 --- /dev/null +++ b/app/src/main/java/com/quranapp/android/utils/extensions/RetrofitResponse.kt @@ -0,0 +1,23 @@ +package com.quranapp.android.utils.extensions + +import com.google.common.net.HttpHeaders +import com.quranapp.android.utils.Log +import okhttp3.ResponseBody +import retrofit2.Response +import java.io.InputStream + +@Throws(Exception::class) +fun Response.getContentLengthAndStream(): Pair { + return Pair( + headers()[HttpHeaders.CONTENT_LENGTH]?.toLong() ?: 0L, + body()?.byteStream() ?: throw Exception("No content") + ) +} + +@Throws(Exception::class) +fun ResponseBody.getContentLengthAndStream(): Pair { + return Pair( + contentLength(), + byteStream() + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/quranapp/android/utils/reader/QuranScriptUtils.kt b/app/src/main/java/com/quranapp/android/utils/reader/QuranScriptUtils.kt index 6199d094..c04dc51a 100644 --- a/app/src/main/java/com/quranapp/android/utils/reader/QuranScriptUtils.kt +++ b/app/src/main/java/com/quranapp/android/utils/reader/QuranScriptUtils.kt @@ -26,7 +26,7 @@ object QuranScriptUtils { const val SCRIPT_KFQPC_V1 = "kfqpc_v1" const val SCRIPT_DEFAULT = SCRIPT_INDO_PAK - const val TOTAL_DOWNLOAD_PARTS = 3 + const val TOTAL_DOWNLOAD_PARTS = 4 val INDO_PAK_SCRIPT_NAMES = mapOf( "en" to "IndoPak", @@ -37,6 +37,7 @@ object QuranScriptUtils { "es" to "IndoPak", "fa" to "هند پاک", "fr" to "IndoPak", + "gu" to "ઈન્ડોપાક", "hi" to "इंडो पाक", "in" to "IndoPak", "it" to "IndoPak", @@ -55,6 +56,7 @@ object QuranScriptUtils { "es" to "Uthmani Hafs", "fa" to "عثمانی حفص", "fr" to "Uthmani Hafs", + "gu" to "ઉથમાની હાફ્સ", "hi" to "उशमनी हफ्स", "in" to "Utsmani Hafs", "it" to "Uthmani Hafs", @@ -73,6 +75,7 @@ object QuranScriptUtils { "es" to "Rey Fahd Complex V1", "fa" to "مجتمع شاه فهد V1", "fr" to "Complexe Roi Fahad V1", + "gu" to "કિંગ ફહદ કોમ્પ્લેક્સ V1", "hi" to "राजा फहद कॉम्प्लेक्स v1", "in" to "Kompleks Raja Fahad V1", "it" to "Complesso di Re Fahad V1", diff --git a/app/src/main/java/com/quranapp/android/utils/services/KFQPCScriptFontsDownloadService.kt b/app/src/main/java/com/quranapp/android/utils/services/KFQPCScriptFontsDownloadService.kt index ebed3e9a..53e6f826 100644 --- a/app/src/main/java/com/quranapp/android/utils/services/KFQPCScriptFontsDownloadService.kt +++ b/app/src/main/java/com/quranapp/android/utils/services/KFQPCScriptFontsDownloadService.kt @@ -16,7 +16,7 @@ import com.quranapp.android.activities.readerSettings.ActivitySettings import com.quranapp.android.api.RetrofitInstance import com.quranapp.android.components.quran.QuranMeta import com.quranapp.android.utils.Log -import com.quranapp.android.utils.Logger +import com.quranapp.android.utils.extensions.getContentLengthAndStream import com.quranapp.android.utils.reader.QuranScriptUtils import com.quranapp.android.utils.reader.getQuranScriptName import com.quranapp.android.utils.reader.toKFQPCFontFilename @@ -28,8 +28,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.io.File import java.io.InputStream import java.io.OutputStream @@ -45,7 +45,7 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { var STARTED_BY_USER = false } - private var job: Job? = null + private var job = Job() private val binder = LocalBinder() private val notifManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } var isDownloadRunning = false @@ -82,7 +82,7 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { override fun onDestroy() { super.onDestroy() - job?.cancel(CancellationException("Cancelled on destroy")) + job.cancel(CancellationException("Cancelled on destroy")) tmpFiles.forEach { it.delete() } tmpFiles.clear() isDownloadRunning = false @@ -115,15 +115,17 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { val ctx = this isDownloadRunning = true - job = lifecycleScope.launch(Dispatchers.IO) { + lifecycleScope.launch(Dispatchers.Main + job) { flow { val fileUtils = FileUtils.newInstance(ctx) + val scriptFile = fileUtils.getScriptFile(scriptKey) try { - downloadScript(this@flow, fileUtils, scriptKey) + downloadScript(this@flow, fileUtils, scriptFile, scriptKey) } catch (e: Exception) { emit(DownloadFlow.Failed(null)) e.printStackTrace() + return@flow } val fontsDir = fileUtils.getKFQPCScriptFontDir(scriptKey) @@ -135,7 +137,7 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { } emit(DownloadFlow.Complete(ALL_PART_DOWNLOADS_FINISHED)) - } + }.flowOn(Dispatchers.IO) .flowWithLifecycle(lifecycle) .catch { Log.saveError(it, "KFQPCScriptFontsDownloadService") @@ -165,40 +167,42 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { } } - private suspend fun downloadScript(flow: FlowCollector, fileUtils: FileUtils, scriptKey: String) { - val scriptFile = fileUtils.getScriptFile(scriptKey) + private suspend fun downloadScript( + flow: FlowCollector, + fileUtils: FileUtils, + scriptFile: File, + scriptKey: String + ) { - if (scriptFile.length() == 0L) { - if (!fileUtils.createFile(scriptFile)) { - flow.emit(DownloadFlow.Failed(null)) - return - } + if (scriptFile.length() > 0) return - flow.emit(DownloadFlow.Start(null)) - flow.emit(DownloadFlow.Progress(null, 0)) + if (!fileUtils.createFile(scriptFile)) { + flow.emit(DownloadFlow.Failed(null)) + return + } - val scriptResBody = RetrofitInstance.github.getQuranScript( - "script_$scriptKey.json" - ) - val byteStream = scriptResBody.byteStream() + flow.emit(DownloadFlow.Start(null)) + flow.emit(DownloadFlow.Progress(null, 0)) - val totalBytes = withContext(Dispatchers.IO) { byteStream.available() } + val (totalBytes, byteStream) = RetrofitInstance.github.getQuranScript( + "script_$scriptKey.json" + ).getContentLengthAndStream() - readStreams( - flow, - null, - byteStream, - scriptFile.outputStream(), - totalBytes - ) + readStreams( + flow, + null, + byteStream, + scriptFile.outputStream(), + totalBytes + ) - flow.emit(DownloadFlow.Complete(null)) - } + flow.emit(DownloadFlow.Complete(null)) } private fun getSkipPartNumber(fontsDir: File): Int { + val totalPages = QuranMeta.totalPages() var totalDownloaded = 0 - for (pageNo in 1..QuranMeta.totalPages()) { + for (pageNo in 1..totalPages) { if (File(fontsDir, pageNo.toKFQPCFontFilename()).length() == 0L) { break } @@ -206,12 +210,16 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { totalDownloaded++ } - val fontsInSingleZip = 200 - + val fontsInSingleZip = totalPages / QuranScriptUtils.TOTAL_DOWNLOAD_PARTS return (totalDownloaded / fontsInSingleZip) + 1 } - private suspend fun downloadFontsPart(flow: FlowCollector, scriptKey: String, partNo: Int, fontsDir: File) { + private suspend fun downloadFontsPart( + flow: FlowCollector, + scriptKey: String, + partNo: Int, + fontsDir: File + ) { val partFilename = "$scriptKey-$partNo.zip" val partFile = File.createTempFile("tmp", partFilename, filesDir) tmpFiles.add(partFile) @@ -219,11 +227,10 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { flow.emit(DownloadFlow.Start(partNo)) flow.emit(DownloadFlow.Progress(partNo, 0)) - val byteStream = RetrofitInstance.github.getKFQPCFont(scriptKey, partFilename).byteStream() - - val totalBytes = withContext(Dispatchers.IO) { - byteStream.available() - } + val (totalBytes, byteStream) = RetrofitInstance.github.getKFQPCFont( + scriptKey, + partFilename + ).getContentLengthAndStream() readStreams( flow, @@ -234,7 +241,6 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { ) extractFonts(partFile, fontsDir) - flow.emit(DownloadFlow.Complete(partNo)) } @@ -268,7 +274,7 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { partNo: Int?, byteStream: InputStream, outStream: OutputStream, - totalBytes: Int + totalBytes: Long ) { byteStream.use { inS -> outStream.use { outS -> @@ -283,10 +289,7 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { outS.write(buffer, 0, bytes) progressBytes += bytes - val progress = ((progressBytes * 100) / totalBytes).toInt() - - Logger.print("Part $partNo: Progress $progress%") - + val progress = if (totalBytes > 0) ((progressBytes * 100) / totalBytes).toInt() else 0 flowCollector.emit( DownloadFlow.Progress(partNo, progress) ) @@ -349,7 +352,7 @@ class KFQPCScriptFontsDownloadService : LifecycleService() { } fun cancel() { - job?.cancel(CancellationException("Cancelled by user")) + job.cancel(CancellationException("Cancelled by user")) finish() } diff --git a/app/src/main/java/com/quranapp/android/utils/services/RecitationService.kt b/app/src/main/java/com/quranapp/android/utils/services/RecitationService.kt index 6328c654..1acad55d 100644 --- a/app/src/main/java/com/quranapp/android/utils/services/RecitationService.kt +++ b/app/src/main/java/com/quranapp/android/utils/services/RecitationService.kt @@ -16,20 +16,8 @@ import android.widget.Toast import androidx.core.graphics.drawable.toBitmap import androidx.core.net.toUri import androidx.core.util.Pair -import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.PlaybackException -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AUTO_TRANSITION -import com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK -import com.google.android.exoplayer2.Player.Listener -import com.google.android.exoplayer2.Player.PositionInfo -import com.google.android.exoplayer2.Player.REPEAT_MODE_OFF -import com.google.android.exoplayer2.Player.REPEAT_MODE_ONE -import com.google.android.exoplayer2.Player.STATE_BUFFERING -import com.google.android.exoplayer2.Player.STATE_ENDED -import com.google.android.exoplayer2.Player.STATE_READY +import com.google.android.exoplayer2.* +import com.google.android.exoplayer2.Player.* import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.source.ConcatenatingMediaSource @@ -315,8 +303,8 @@ class RecitationService : Service(), MediaDescriptionAdapter { recPlayer?.onPlayMedia(recParams) } - fun onChapterChanged(chapterNo: Int, fromVerse: Int, toVerse: Int) { - recParams.currentVerse = ChapterVersePair(chapterNo, fromVerse) + fun onChapterChanged(chapterNo: Int, fromVerse: Int, toVerse: Int, currentVerse: Int) { + recParams.currentVerse = ChapterVersePair(chapterNo, currentVerse) recParams.firstVerse = ChapterVersePair(chapterNo, fromVerse) recParams.lastVerse = ChapterVersePair(chapterNo, toVerse) recParams.currentReciter = SPReader.getSavedRecitationSlug(this) diff --git a/app/src/main/java/com/quranapp/android/utils/services/TranslationDownloadService.kt b/app/src/main/java/com/quranapp/android/utils/services/TranslationDownloadService.kt index b7c99892..62523b0b 100644 --- a/app/src/main/java/com/quranapp/android/utils/services/TranslationDownloadService.kt +++ b/app/src/main/java/com/quranapp/android/utils/services/TranslationDownloadService.kt @@ -22,6 +22,7 @@ import com.quranapp.android.components.quran.subcomponents.QuranTranslBookInfo import com.quranapp.android.utils.Log import com.quranapp.android.utils.app.AppActions import com.quranapp.android.utils.app.NotificationUtils +import com.quranapp.android.utils.extensions.getContentLengthAndStream import com.quranapp.android.utils.extensions.serializableExtra import com.quranapp.android.utils.reader.factory.QuranTranslationFactory import com.quranapp.android.utils.receivers.TranslDownloadReceiver @@ -34,6 +35,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch +import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream import kotlin.io.path.readText @@ -119,19 +121,20 @@ class TranslationDownloadService : Service() { ) { CoroutineScope(Dispatchers.Main + jobs[bookInfo.slug]!!).launch { val notifId = bookInfo.slug.hashCode() + val tmpFile = kotlin.io.path.createTempFile( + prefix = bookInfo.slug, + suffix = ".json" + ) flow { emit(TranslationDownloadFlow.Start) emit(TranslationDownloadFlow.Progress(0)) - val responseBody = RetrofitInstance.github.getTranslation(bookInfo.downloadPath) - val byteStream = responseBody.byteStream().buffered() - val totalBytes = byteStream.available() - val tmpFile = kotlin.io.path.createTempFile( - prefix = bookInfo.slug, - suffix = ".json" - ) + val (totalBytes, byteStream) = RetrofitInstance.github.getTranslation( + bookInfo.downloadPath + ).getContentLengthAndStream() + byteStream.use { inS -> tmpFile.outputStream().buffered().use { outS -> @@ -146,7 +149,11 @@ class TranslationDownloadService : Service() { outS.write(buffer, 0, bytes) progressBytes += bytes - emit(TranslationDownloadFlow.Progress(((progressBytes * 100) / totalBytes).toInt())) + emit( + TranslationDownloadFlow.Progress( + if (totalBytes > 0) ((progressBytes * 100) / totalBytes).toInt() else 0 + ) + ) } outS.flush() @@ -159,11 +166,9 @@ class TranslationDownloadService : Service() { it.dbHelper.storeTranslation(bookInfo, tmpFile.readText()) } - val slug = bookInfo.slug - - removeFromPendingAction(context, AppActions.APP_ACTION_TRANSL_UPDATE, slug) + removeFromPendingAction(context, AppActions.APP_ACTION_TRANSL_UPDATE, bookInfo.slug) val savedTranslations = SPReader.getSavedTranslations(context) - if (savedTranslations.remove(slug)) { + if (savedTranslations.remove(bookInfo.slug)) { SPReader.setSavedTranslations(context, savedTranslations) } @@ -188,11 +193,13 @@ class TranslationDownloadService : Service() { } is TranslationDownloadFlow.Complete -> { + tmpFile.deleteIfExists() sendStatusBroadcast(TranslDownloadReceiver.TRANSL_DOWNLOAD_STATUS_SUCCEED, bookInfo) finish() } is TranslationDownloadFlow.Failed -> { + tmpFile.deleteIfExists() sendStatusBroadcast(TranslDownloadReceiver.TRANSL_DOWNLOAD_STATUS_FAILED, bookInfo) removeDownload(bookInfo.slug) notifManager.cancel(notifId) diff --git a/app/src/main/java/com/quranapp/android/views/recitation/RecitationPlayer.kt b/app/src/main/java/com/quranapp/android/views/recitation/RecitationPlayer.kt index 6a2398f7..b4c3296f 100644 --- a/app/src/main/java/com/quranapp/android/views/recitation/RecitationPlayer.kt +++ b/app/src/main/java/com/quranapp/android/views/recitation/RecitationPlayer.kt @@ -46,10 +46,16 @@ class RecitationPlayer( initControls() } - fun onChapterChanged(chapterNo: Int, fromVerse: Int, toVerse: Int, preventStop: Boolean) { + fun onChapterChanged( + chapterNo: Int, + fromVerse: Int, + toVerse: Int, + currentVerse: Int, + preventStop: Boolean + ) { readerChanging = true - service?.onChapterChanged(chapterNo, fromVerse, toVerse) + service?.onChapterChanged(chapterNo, fromVerse, toVerse, currentVerse) onReaderChanged(preventStop) diff --git a/inventory/fonts/kfqpc_v1/kfqpc_v1-1.zip b/inventory/fonts/kfqpc_v1/kfqpc_v1-1.zip index 9c6f9b54..2f543561 100644 Binary files a/inventory/fonts/kfqpc_v1/kfqpc_v1-1.zip and b/inventory/fonts/kfqpc_v1/kfqpc_v1-1.zip differ diff --git a/inventory/fonts/kfqpc_v1/kfqpc_v1-2.zip b/inventory/fonts/kfqpc_v1/kfqpc_v1-2.zip index 8abfcfab..03d3a74a 100644 Binary files a/inventory/fonts/kfqpc_v1/kfqpc_v1-2.zip and b/inventory/fonts/kfqpc_v1/kfqpc_v1-2.zip differ diff --git a/inventory/fonts/kfqpc_v1/kfqpc_v1-3.zip b/inventory/fonts/kfqpc_v1/kfqpc_v1-3.zip index eb12175c..b59662b8 100644 Binary files a/inventory/fonts/kfqpc_v1/kfqpc_v1-3.zip and b/inventory/fonts/kfqpc_v1/kfqpc_v1-3.zip differ diff --git a/inventory/fonts/kfqpc_v1/kfqpc_v1-4.zip b/inventory/fonts/kfqpc_v1/kfqpc_v1-4.zip new file mode 100644 index 00000000..ddc206a4 Binary files /dev/null and b/inventory/fonts/kfqpc_v1/kfqpc_v1-4.zip differ diff --git a/inventory/versions/resources_versions.json b/inventory/versions/resources_versions.json index fdefc535..ba2dad08 100644 --- a/inventory/versions/resources_versions.json +++ b/inventory/versions/resources_versions.json @@ -1,6 +1,6 @@ { "urls": 2, - "translations": 10, + "translations": 11, "recitations": 6, "recitationTranslations": 2, "tafsirs": 1