package com.quran.labs.androidquran.model.translation; import android.content.Context; import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import com.quran.labs.androidquran.common.QuranText; import com.quran.labs.androidquran.dao.Bookmark; import com.quran.labs.androidquran.data.QuranDataProvider; import com.quran.labs.androidquran.data.QuranInfo; import com.quran.labs.androidquran.data.SuraAyah; import com.quran.labs.androidquran.database.DatabaseHandler; import com.quran.labs.androidquran.database.DatabaseUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import io.reactivex.Single; import io.reactivex.schedulers.Schedulers; @Singleton public class ArabicDatabaseUtils { public static final String AR_BASMALLAH = "بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ"; @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) static final int NUMBER_OF_WORDS = 4; private final Context appContext; private DatabaseHandler arabicDatabaseHandler; @Inject ArabicDatabaseUtils(Context context) { this.appContext = context; arabicDatabaseHandler = getArabicDatabaseHandler(); } DatabaseHandler getArabicDatabaseHandler() { if (arabicDatabaseHandler == null) { try { arabicDatabaseHandler = DatabaseHandler.getDatabaseHandler( appContext.getApplicationContext(), QuranDataProvider.QURAN_ARABIC_DATABASE); } catch (Exception e) { // ignore } } return arabicDatabaseHandler; } @NonNull public Single<List<QuranText>> getVerses(final SuraAyah start, final SuraAyah end) { return Single.fromCallable(() -> { List<QuranText> verses = new ArrayList<>(); Cursor cursor = null; try { DatabaseHandler arabicDatabaseHandler = getArabicDatabaseHandler(); cursor = arabicDatabaseHandler.getVerses(start.sura, start.ayah, end.sura, end.ayah, DatabaseHandler.ARABIC_TEXT_TABLE); while (cursor.moveToNext()) { QuranText verse = new QuranText(cursor.getInt(1), cursor.getInt(2), cursor.getString(3)); verses.add(verse); } } catch (Exception e) { // no op } finally { DatabaseUtils.closeCursor(cursor); } return verses; }).subscribeOn(Schedulers.io()); } public List<Bookmark> hydrateAyahText(List<Bookmark> bookmarks) { List<Integer> ayahIds = new ArrayList<>(); for (int i = 0, bookmarksSize = bookmarks.size(); i < bookmarksSize; i++) { Bookmark bookmark = bookmarks.get(i); if (!bookmark.isPageBookmark()) { ayahIds.add(QuranInfo.getAyahId(bookmark.sura, bookmark.ayah)); } } return ayahIds.isEmpty() ? bookmarks : mergeBookmarksWithAyahText(bookmarks, getAyahTextForAyat(ayahIds)); } Map<Integer, String> getAyahTextForAyat(List<Integer> ayat) { Map<Integer, String> result = new HashMap<>(ayat.size()); DatabaseHandler arabicDatabaseHandler = getArabicDatabaseHandler(); if (arabicDatabaseHandler != null) { Cursor cursor = null; try { cursor = arabicDatabaseHandler.getVersesByIds(ayat); while (cursor.moveToNext()) { int id = cursor.getInt(0); int sura = cursor.getInt(1); int ayah = cursor.getInt(2); String text = cursor.getString(3); result.put(id, getFirstFewWordsFromAyah(sura, ayah, text)); } } finally { DatabaseUtils.closeCursor(cursor); } } return result; } private List<Bookmark> mergeBookmarksWithAyahText( List<Bookmark> bookmarks, Map<Integer, String> ayahMap) { List<Bookmark> result; if (ayahMap.isEmpty()) { result = bookmarks; } else { result = new ArrayList<>(bookmarks.size()); for (int i = 0, bookmarksSize = bookmarks.size(); i < bookmarksSize; i++) { Bookmark bookmark = bookmarks.get(i); Bookmark toAdd; if (bookmark.isPageBookmark()) { toAdd = bookmark; } else { String ayahText = ayahMap.get(QuranInfo.getAyahId(bookmark.sura, bookmark.ayah)); toAdd = ayahText == null ? bookmark : bookmark.withAyahText(ayahText); } result.add(toAdd); } } return result; } static String getFirstFewWordsFromAyah(int sura, int ayah, String text) { String ayahText = getAyahWithoutBasmallah(sura, ayah, text); int start = 0; for (int i = 0; i < NUMBER_OF_WORDS && start > -1; i++) { start = ayahText.indexOf(' ', start + 1); } return start > 0 ? ayahText.substring(0, start) : ayahText; } /** * Get the actual ayahText from the given ayahText. This is important because, currently, the * arabic database (quran.ar.db) has the first verse from each sura as "[basmallah] ayah" - this * method just returns ayah without basmallah. * * @param sura the sura number * @param ayah the ayah number * @param ayahText the ayah text * @return the ayah without the basmallah */ public static String getAyahWithoutBasmallah(int sura, int ayah, String ayahText) { // note that ayahText.startsWith check is always true for now - but it's explicitly here so // that if we update quran.ar.db one day to fix this issue and older clients get a new copy of // the database, their code continues to work as before. if (ayah == 1 && sura != 9 && sura != 1 && ayahText.startsWith(AR_BASMALLAH)) { return ayahText.substring(AR_BASMALLAH.length() + 1); } return ayahText; } }