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