-
Notifications
You must be signed in to change notification settings - Fork 890
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
43 changed files
with
464 additions
and
322 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
app/src/main/java/com/quran/labs/androidquran/common/LocalTranslationDisplaySort.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
176 changes: 52 additions & 124 deletions
176
app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,163 +1,91 @@ | ||
package com.quran.labs.androidquran.database | ||
|
||
import android.content.ContentValues | ||
import android.content.Context | ||
import android.database.Cursor | ||
import android.database.sqlite.SQLiteDatabase | ||
import android.util.SparseArray | ||
import androidx.annotation.WorkerThread | ||
import com.quran.labs.androidquran.common.LocalTranslation | ||
import com.quran.labs.androidquran.dao.translation.TranslationItem | ||
import com.quran.labs.androidquran.database.TranslationsDBHelper.TranslationsTable | ||
import com.quran.labs.androidquran.util.QuranFileUtils | ||
import com.quran.mobile.di.qualifier.ApplicationContext | ||
import timber.log.Timber | ||
import java.util.Collections | ||
import com.quran.mobile.translation.data.TranslationsDataSource | ||
import com.quran.mobile.translation.model.LocalTranslation | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.filterNotNull | ||
import kotlinx.coroutines.flow.first | ||
import kotlinx.coroutines.flow.map | ||
import kotlinx.coroutines.runBlocking | ||
import javax.inject.Inject | ||
import javax.inject.Singleton | ||
|
||
@Singleton | ||
class TranslationsDBAdapter @Inject constructor( | ||
@ApplicationContext private val context: Context, | ||
adapter: TranslationsDBHelper, | ||
private val dataSource: TranslationsDataSource, | ||
private val quranFileUtils: QuranFileUtils | ||
) { | ||
private val db: SQLiteDatabase = adapter.writableDatabase | ||
|
||
@Volatile | ||
private var cachedTranslations: List<LocalTranslation>? = null | ||
fun getTranslations(): Flow<List<LocalTranslation>> { | ||
return dataSource.translations() | ||
.filterNotNull() | ||
.map { translations -> | ||
translations.filter { quranFileUtils.hasTranslation(context, it.filename) } | ||
} | ||
} | ||
|
||
var lastWriteTime: Long = 0 | ||
private set | ||
@WorkerThread | ||
fun legacyGetTranslations(): List<LocalTranslation> { | ||
return runBlocking { getTranslations().first() } | ||
} | ||
|
||
@WorkerThread | ||
fun getTranslationsHash(): SparseArray<LocalTranslation> { | ||
val result = SparseArray<LocalTranslation>() | ||
for (item in getTranslations()) { | ||
result.put(item.id, item) | ||
for (item in legacyGetTranslations()) { | ||
result.put(item.id.toInt(), item) | ||
} | ||
return result | ||
} | ||
|
||
// intentional, since cachedTranslations can be replaced by another thread, causing the check | ||
// to be true, but the cached object returned to be null (or to change). | ||
@WorkerThread | ||
fun getTranslations(): List<LocalTranslation> { | ||
// intentional, since cachedTranslations can be replaced by another thread, causing the check | ||
// to be true, but the cached object returned to be null (or to change). | ||
val cached = cachedTranslations | ||
if (!cached.isNullOrEmpty()) { | ||
return cached | ||
} | ||
var items: MutableList<LocalTranslation> = ArrayList() | ||
var cursor: Cursor? = null | ||
try { | ||
cursor = db.query(TranslationsTable.TABLE_NAME, | ||
null, null, null, null, null, | ||
TranslationsTable.ID + " ASC") | ||
|
||
while (cursor.moveToNext()) { | ||
val id = cursor.getInt(0) | ||
val name = cursor.getString(1) | ||
val translator = cursor.getString(2) | ||
val translatorForeign = cursor.getString(3) | ||
val filename = cursor.getString(4) | ||
val url = cursor.getString(5) | ||
val languageCode = cursor.getString(6) | ||
val version = cursor.getInt(7) | ||
val minimumVersion = cursor.getInt(8) | ||
val displayOrder = cursor.getInt(9) | ||
|
||
if (quranFileUtils.hasTranslation(context, filename)) { | ||
items.add( | ||
LocalTranslation( | ||
id, filename, name, translator, | ||
translatorForeign, url, languageCode, version, minimumVersion, displayOrder | ||
) | ||
) | ||
} | ||
} | ||
} finally { | ||
cursor?.close() | ||
} | ||
suspend fun deleteTranslationByFileName(filename: String) { | ||
dataSource.removeTranslation(filename) | ||
} | ||
|
||
items = Collections.unmodifiableList(items) | ||
if (items.size > 0) { | ||
cachedTranslations = items | ||
@WorkerThread | ||
fun legacyDeleteTranslationByFileName(filename: String) { | ||
runBlocking { | ||
deleteTranslationByFileName(filename) | ||
} | ||
return items | ||
} | ||
|
||
fun deleteTranslationByFile(filename: String) { | ||
db.execSQL("DELETE FROM " + TranslationsTable.TABLE_NAME + " WHERE " + | ||
TranslationsTable.FILENAME + " = ?", arrayOf<Any>(filename)) | ||
@WorkerThread | ||
fun legacyWriteTranslationUpdates(updates: List<TranslationItem>): Boolean { | ||
return runBlocking { writeTranslationUpdates(updates) } | ||
} | ||
|
||
fun writeTranslationUpdates(updates: List<TranslationItem>): Boolean { | ||
var result = true | ||
db.beginTransaction() | ||
suspend fun writeTranslationUpdates(updates: List<TranslationItem>): Boolean { | ||
val (available, unavailable) = updates.partition { it.exists() } | ||
|
||
try { | ||
var cachedNextOrder = -1 | ||
for (item in updates) { | ||
if (item.exists()) { | ||
var displayOrder = 0 | ||
val translation = item.translation | ||
if (item.displayOrder > -1) { | ||
displayOrder = item.displayOrder | ||
} else { | ||
var cursor: Cursor? = null | ||
if (cachedNextOrder == -1) { | ||
try { | ||
// get next highest display order | ||
cursor = db.query( | ||
TranslationsTable.TABLE_NAME, arrayOf(TranslationsTable.DISPLAY_ORDER), | ||
null, null, null, null, | ||
TranslationsTable.DISPLAY_ORDER + " DESC", | ||
"1" | ||
) | ||
if (cursor != null && cursor.moveToFirst()) { | ||
cachedNextOrder = cursor.getInt(0) + 1 | ||
displayOrder = cachedNextOrder++ | ||
} | ||
} finally { | ||
cursor?.close() | ||
} | ||
} else { | ||
displayOrder = cachedNextOrder++ | ||
} | ||
} | ||
|
||
val values = ContentValues() | ||
values.put(TranslationsTable.ID, translation.id) | ||
values.put(TranslationsTable.NAME, translation.displayName) | ||
values.put(TranslationsTable.TRANSLATOR, translation.translator) | ||
values.put(TranslationsTable.TRANSLATOR_FOREIGN, | ||
translation.translatorNameLocalized) | ||
values.put(TranslationsTable.FILENAME, translation.fileName) | ||
values.put(TranslationsTable.URL, translation.fileUrl) | ||
values.put(TranslationsTable.LANGUAGE_CODE, translation.languageCode) | ||
values.put(TranslationsTable.VERSION, item.localVersion) | ||
values.put(TranslationsTable.MINIMUM_REQUIRED_VERSION, translation.minimumVersion) | ||
values.put(TranslationsTable.DISPLAY_ORDER, displayOrder) | ||
val needNextOrder = available.any { it.displayOrder == -1 } | ||
val nextOrder = if (needNextOrder) { | ||
dataSource.maximumDisplayOrder().toInt() + 1 | ||
} else { | ||
available.maxOf { it.displayOrder } + 1 | ||
} | ||
|
||
db.replace(TranslationsTable.TABLE_NAME, null, values) | ||
val items = if (needNextOrder) { | ||
var nextOrderNumber = nextOrder | ||
available.map { item -> | ||
if (item.displayOrder == -1) { | ||
item.copy(displayOrder = nextOrderNumber++) | ||
} else { | ||
db.delete(TranslationsTable.TABLE_NAME, | ||
TranslationsTable.ID + " = " + item.translation.id, null) | ||
item | ||
} | ||
} | ||
db.setTransactionSuccessful() | ||
|
||
lastWriteTime = System.currentTimeMillis() | ||
// clear the cached translations | ||
cachedTranslations = null | ||
} catch (e: Exception) { | ||
result = false | ||
Timber.d(e, "error writing translation updates") | ||
} finally { | ||
db.endTransaction() | ||
} else { | ||
available | ||
} | ||
|
||
return result | ||
dataSource.updateTranslations(items.map { it.asLocalTranslation() }) | ||
dataSource.removeTranslationsById(unavailable.map { it.translation.id.toLong() }) | ||
|
||
return true | ||
} | ||
} |
Oops, something went wrong.