diff --git a/app/src/main/java/org/zotero/android/androidx/content/Context.kt b/app/src/main/java/org/zotero/android/androidx/content/Context.kt index bcbdab70..6f9b97e4 100644 --- a/app/src/main/java/org/zotero/android/androidx/content/Context.kt +++ b/app/src/main/java/org/zotero/android/androidx/content/Context.kt @@ -15,6 +15,7 @@ import androidx.annotation.AttrRes import androidx.annotation.DimenRes import androidx.annotation.StringRes import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalDensity import androidx.core.content.ContextCompat import timber.log.Timber @@ -97,5 +98,17 @@ fun Context.getFileSize(uri: Uri): Long? { } } +@Composable +fun Context.getDrawableByItemType(typeIconString: String): Int { + val drawableId = remember(typeIconString) { + resources.getIdentifier( + typeIconString, + "drawable", + packageName + ) + } + return drawableId +} + @Composable fun Int.pxToDp() = with(LocalDensity.current) { this@pxToDp.toDp() } \ No newline at end of file diff --git a/app/src/main/java/org/zotero/android/database/Database.kt b/app/src/main/java/org/zotero/android/database/Database.kt index a177075e..2522a4fe 100644 --- a/app/src/main/java/org/zotero/android/database/Database.kt +++ b/app/src/main/java/org/zotero/android/database/Database.kt @@ -1,34 +1,141 @@ package org.zotero.android.database +import io.realm.DynamicRealm +import io.realm.FieldAttribute import io.realm.RealmConfiguration +import io.realm.annotations.RealmModule +import org.zotero.android.database.objects.AllItemsDbRow +import org.zotero.android.database.objects.FieldKeys +import org.zotero.android.database.objects.ItemTypes +import org.zotero.android.database.objects.RCollection +import org.zotero.android.database.objects.RCondition +import org.zotero.android.database.objects.RCreator +import org.zotero.android.database.objects.RCustomLibrary +import org.zotero.android.database.objects.RGroup +import org.zotero.android.database.objects.RItem +import org.zotero.android.database.objects.RItemField +import org.zotero.android.database.objects.RLink +import org.zotero.android.database.objects.RObjectChange +import org.zotero.android.database.objects.RPageIndex +import org.zotero.android.database.objects.RPath +import org.zotero.android.database.objects.RPathCoordinate +import org.zotero.android.database.objects.RRect +import org.zotero.android.database.objects.RRelation +import org.zotero.android.database.objects.RSearch +import org.zotero.android.database.objects.RTag +import org.zotero.android.database.objects.RTranslatorMetadata +import org.zotero.android.database.objects.RTypedTag +import org.zotero.android.database.objects.RUser +import org.zotero.android.database.objects.RVersions +import org.zotero.android.database.objects.RWebDavDeletion +import org.zotero.android.database.requests.key import org.zotero.android.files.FileStore import java.io.File class Database { companion object { - private const val schemaVersion = 1L //From now on must only increase by 1 whenever db schema changes + private const val schemaVersion = 2L //From now on must only increase by 1 whenever db schema changes fun mainConfiguration(dbFile: File): RealmConfiguration { val builder = RealmConfiguration.Builder() .directory(dbFile.parentFile!!) .name(dbFile.name) + .modules(MainConfigurationDbModule()) .schemaVersion(schemaVersion) .allowWritesOnUiThread(true) - .deleteRealmIfMigrationNeeded() + .migration { dynamicRealm, oldVersion, newVersion -> + if (oldVersion < 2) { + migrateAllItemsDbRowTypeIconNameTypeChange(dynamicRealm) + } + } return builder.build() } + private fun migrateAllItemsDbRowTypeIconNameTypeChange(dynamicRealm: DynamicRealm) { + val realmSchema = dynamicRealm.schema + + val allItemsDbRowSchema = realmSchema.get(AllItemsDbRow::class.java.simpleName) + + allItemsDbRowSchema?.run { + removeField("typeIconName") + addField("typeIconName", String::class.java, FieldAttribute.REQUIRED) + transform { + it.set("typeIconName", "") + } + } + val allItems = dynamicRealm.where(RItem::class.java.simpleName).findAll() + for (item in allItems) { + val dbRow = item.getObject("allItemsDbRow") + if (dbRow == null) { + println() + continue + } + + val rawType = item.getString("rawType") + val fields = item.getList("fields") + + val contentType = if (rawType == ItemTypes.attachment) fields.where().key( + FieldKeys.Item.Attachment.contentType + ).findFirst()?.getString("value") else null + dbRow.setString( + "typeIconName", ItemTypes.iconName( + rawType = rawType, + contentType = contentType + ) + ) + } + + } + fun bundledDataConfiguration(fileStorage: FileStore): RealmConfiguration { val dbFile = fileStorage.bundledDataDbFile() val builder = RealmConfiguration.Builder() .directory(dbFile.parentFile!!) .name(dbFile.name) + .modules(BundledDataConfigurationDbModule()) .schemaVersion(schemaVersion) .allowWritesOnUiThread(true) - .deleteRealmIfMigrationNeeded() - + .migration { dynamicRealm, oldVersion, newVersion -> + //no-op for bundle migration for now + } return builder.build() } } +} + +@RealmModule( + library = false, classes = [ + RCollection::class, + RCreator::class, + RCustomLibrary::class, + RGroup::class, + RItem::class, + RItemField::class, + RLink::class, + RPageIndex::class, + RPath::class, + RPathCoordinate::class, + RRect::class, + RRelation::class, + RSearch::class, + RCondition::class, + RTag::class, + RTypedTag::class, + RUser::class, + RWebDavDeletion::class, + RVersions::class, + RObjectChange::class, + AllItemsDbRow::class, + ] +) +data class MainConfigurationDbModule(val placeholder: String) { // empty data class for equals/hashcode + constructor(): this("") +} + +@RealmModule(library = false, classes=[ + RTranslatorMetadata::class +]) +data class BundledDataConfigurationDbModule(val placeholder: String) { // empty data class for equals/hashcode + constructor(): this("") } \ No newline at end of file diff --git a/app/src/main/java/org/zotero/android/database/objects/AllItemsDbRow.kt b/app/src/main/java/org/zotero/android/database/objects/AllItemsDbRow.kt index 15a75661..fb0e8481 100644 --- a/app/src/main/java/org/zotero/android/database/objects/AllItemsDbRow.kt +++ b/app/src/main/java/org/zotero/android/database/objects/AllItemsDbRow.kt @@ -5,7 +5,7 @@ import io.realm.annotations.RealmClass @RealmClass(embedded = true) open class AllItemsDbRow : RealmObject() { - var typeIconName: Int = 0 + var typeIconName: String = "" var title: String = "" var subtitle: String = "" var hasNote: Boolean = false diff --git a/app/src/main/java/org/zotero/android/database/objects/ItemTypes.kt b/app/src/main/java/org/zotero/android/database/objects/ItemTypes.kt index 8bbf60ac..4b08028c 100644 --- a/app/src/main/java/org/zotero/android/database/objects/ItemTypes.kt +++ b/app/src/main/java/org/zotero/android/database/objects/ItemTypes.kt @@ -1,7 +1,5 @@ package org.zotero.android.database.objects -import org.zotero.android.uicomponents.Drawables - class ItemTypes { companion object { const val note = "note" @@ -15,121 +13,121 @@ class ItemTypes { var excludedFromTypePicker: Set = setOf(attachment, annotation) - fun iconName(rawType: String, contentType: String?): Int { + fun iconName(rawType: String, contentType: String?): String { when (rawType) { "artwork" -> { - return Drawables.item_type_artwork + return "item_type_artwork" } "attachment" -> { if (contentType?.contains("pdf") == true) { - return Drawables.item_type_pdf + return "item_type_pdf" } - return Drawables.item_type_document + return "item_type_document" } "audioRecording" -> { - return Drawables.item_type_audiorecording + return "item_type_audiorecording" } "book" -> { - return Drawables.item_type_book + return "item_type_book" } "bookSection" -> { - return Drawables.item_type_booksection + return "item_type_booksection" } "bill" -> { - return Drawables.item_type_bill + return "item_type_bill" } "blogPost" -> { - return Drawables.item_type_blogpost + return "item_type_blogpost" } "case" -> { - return Drawables.item_type_case + return "item_type_case" } "computerProgram" -> { - return Drawables.item_type_computerprogram + return "item_type_computerprogram" } "conferencePaper" -> { - return Drawables.item_type_conferencepaper + return "item_type_conferencepaper" } "dictionaryEntry" -> { - return Drawables.item_type_dictionaryentry + return "item_type_dictionaryentry" } "document" -> { - return Drawables.item_type_document + return "item_type_document" } "email" -> { - return Drawables.item_type_email + return "item_type_email" } "encyclopediaArticle" -> { - return Drawables.item_type_encyclopediaarticle + return "item_type_encyclopediaarticle" } "film" -> { - return Drawables.item_type_film + return "item_type_film" } "forumPost" -> { - return Drawables.item_type_forumpost + return "item_type_forumpost" } "hearing" -> { - return Drawables.item_type_hearing + return "item_type_hearing" } "instantMessage" -> { - return Drawables.item_type_instantmessage + return "item_type_instantmessage" } "interview" -> { - return Drawables.item_type_interview + return "item_type_interview" } "journalArticle" -> { - return Drawables.item_type_journalarticle + return "item_type_journalarticle" } "letter" -> { - return Drawables.item_type_letter + return "item_type_letter" } "magazineArticle" -> { - return Drawables.item_type_magazinearticle + return "item_type_magazinearticle" } "map" -> { - return Drawables.item_type_map + return "item_type_map" } "manuscript" -> { - return Drawables.item_type_manuscript + return "item_type_manuscript" } "note" -> { - return Drawables.item_type_note + return "item_type_note" } "newspaperArticle" -> { - return Drawables.item_type_newspaperarticle + return "item_type_newspaperarticle" } "patent" -> { - return Drawables.item_type_patent + return "item_type_patent" } "podcast" -> { - return Drawables.item_type_podcast + return "item_type_podcast" } "presentation" -> { - return Drawables.item_type_presentation + return "item_type_presentation" } "radioBroadcast" -> { - return Drawables.item_type_radiobroadcast + return "item_type_radiobroadcast" } "report" -> { - return Drawables.item_type_report + return "item_type_report" } "statute" -> { - return Drawables.item_type_statute + return "item_type_statute" } "thesis" -> { - return Drawables.item_type_thesis + return "item_type_thesis" } "tvBroadcast" -> { - return Drawables.item_type_tvbroadcast + return "item_type_tvbroadcast" } "videoRecording" -> { - return Drawables.item_type_videorecording + return "item_type_videorecording" } "webpage" -> { - return Drawables.item_type_webpage + return "item_type_webpage" } else -> { - return Drawables.item_type_document + return "item_type_document" } } } diff --git a/app/src/main/java/org/zotero/android/screens/addnote/AddNoteTopBar.kt b/app/src/main/java/org/zotero/android/screens/addnote/AddNoteTopBar.kt index 829fafe6..25693e58 100644 --- a/app/src/main/java/org/zotero/android/screens/addnote/AddNoteTopBar.kt +++ b/app/src/main/java/org/zotero/android/screens/addnote/AddNoteTopBar.kt @@ -9,11 +9,12 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import org.zotero.android.database.objects.ItemTypes +import org.zotero.android.androidx.content.getDrawableByItemType import org.zotero.android.screens.addnote.data.AddOrEditNoteArgs import org.zotero.android.uicomponents.Strings import org.zotero.android.uicomponents.theme.CustomTheme @@ -31,10 +32,10 @@ internal fun AddNoteTopBar( modifier = modifier.padding(start = 40.dp), verticalAlignment = Alignment.CenterVertically ) { - val iconInt = titleData?.type?.let { ItemTypes.iconName(it, null) } - if (iconInt != null) { + val type = titleData?.type + if (type != null) { Image( - painter = painterResource(id = iconInt), + painter = painterResource(id = LocalContext.current.getDrawableByItemType(type)), contentDescription = null, ) Spacer(modifier = Modifier.width(10.dp)) diff --git a/app/src/main/java/org/zotero/android/screens/allitems/AllItemsTable.kt b/app/src/main/java/org/zotero/android/screens/allitems/AllItemsTable.kt index 307bb4e3..89231349 100644 --- a/app/src/main/java/org/zotero/android/screens/allitems/AllItemsTable.kt +++ b/app/src/main/java/org/zotero/android/screens/allitems/AllItemsTable.kt @@ -24,9 +24,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import org.zotero.android.androidx.content.getDrawableByItemType import org.zotero.android.architecture.ui.CustomLayoutSize import org.zotero.android.screens.allitems.data.ItemCellModel import org.zotero.android.uicomponents.Drawables @@ -142,7 +144,7 @@ private fun ItemRowLeftPart( Spacer(modifier = Modifier.width(16.dp)) Image( modifier = Modifier.size(28.dp), - painter = painterResource(id = model.typeIconName), + painter = painterResource(id = LocalContext.current.getDrawableByItemType(model.typeIconName)), contentDescription = null, ) } diff --git a/app/src/main/java/org/zotero/android/screens/allitems/data/ItemCellModel.kt b/app/src/main/java/org/zotero/android/screens/allitems/data/ItemCellModel.kt index 39e086b3..532c6453 100644 --- a/app/src/main/java/org/zotero/android/screens/allitems/data/ItemCellModel.kt +++ b/app/src/main/java/org/zotero/android/screens/allitems/data/ItemCellModel.kt @@ -8,7 +8,7 @@ import org.zotero.android.uicomponents.attachmentprogress.State data class ItemCellModel( val key: String, - val typeIconName: Int, + val typeIconName: String, val typeName: String, val title: String, val subtitle: String, diff --git a/app/src/main/java/org/zotero/android/screens/scanbarcode/ui/ScanBarcodeTable.kt b/app/src/main/java/org/zotero/android/screens/scanbarcode/ui/ScanBarcodeTable.kt index 1a962344..9882e6f2 100644 --- a/app/src/main/java/org/zotero/android/screens/scanbarcode/ui/ScanBarcodeTable.kt +++ b/app/src/main/java/org/zotero/android/screens/scanbarcode/ui/ScanBarcodeTable.kt @@ -16,11 +16,13 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.core.text.HtmlCompat +import org.zotero.android.androidx.content.getDrawableByItemType import org.zotero.android.attachmentdownloader.RemoteAttachmentDownloader import org.zotero.android.database.objects.Attachment import org.zotero.android.database.objects.ItemTypes @@ -87,7 +89,14 @@ internal fun LookupItemRow( ) { Image( modifier = modifier, - painter = painterResource(id = ItemTypes.iconName(type, null)), + painter = painterResource( + id = LocalContext.current.getDrawableByItemType( + ItemTypes.iconName( + type, + null + ) + ) + ), contentDescription = null, ) Column( diff --git a/app/src/main/java/org/zotero/android/screens/share/ShareSections.kt b/app/src/main/java/org/zotero/android/screens/share/ShareSections.kt index d375726c..8a4f7b08 100644 --- a/app/src/main/java/org/zotero/android/screens/share/ShareSections.kt +++ b/app/src/main/java/org/zotero/android/screens/share/ShareSections.kt @@ -14,10 +14,12 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.core.text.HtmlCompat +import org.zotero.android.androidx.content.getDrawableByItemType import org.zotero.android.api.pojo.sync.ItemResponse import org.zotero.android.database.objects.Attachment import org.zotero.android.database.objects.ItemTypes @@ -140,7 +142,12 @@ private fun SetAttachment(title: String, file: File, state: AttachmentState, sho private fun SetItem(title: String, type: String) { ParsedShareItem( title = title, - iconInt = ItemTypes.iconName(type, null) + iconInt = LocalContext.current.getDrawableByItemType( + ItemTypes.iconName( + type, + null + ) + ) ) } diff --git a/app/src/main/java/org/zotero/android/uicomponents/addbyidentifier/ui/AddByIdentifierTable.kt b/app/src/main/java/org/zotero/android/uicomponents/addbyidentifier/ui/AddByIdentifierTable.kt index f198fddd..384508e7 100644 --- a/app/src/main/java/org/zotero/android/uicomponents/addbyidentifier/ui/AddByIdentifierTable.kt +++ b/app/src/main/java/org/zotero/android/uicomponents/addbyidentifier/ui/AddByIdentifierTable.kt @@ -16,11 +16,13 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.core.text.HtmlCompat +import org.zotero.android.androidx.content.getDrawableByItemType import org.zotero.android.attachmentdownloader.RemoteAttachmentDownloader import org.zotero.android.database.objects.Attachment import org.zotero.android.database.objects.ItemTypes @@ -84,7 +86,14 @@ internal fun LookupItemRow( ) { Image( modifier = modifier, - painter = painterResource(id = ItemTypes.iconName(type, null)), + painter = painterResource( + id = LocalContext.current.getDrawableByItemType( + ItemTypes.iconName( + type, + null + ) + ) + ), contentDescription = null, ) Column( diff --git a/buildSrc/src/main/kotlin/BuildConfig.kt b/buildSrc/src/main/kotlin/BuildConfig.kt index 845a3e5c..54004be1 100644 --- a/buildSrc/src/main/kotlin/BuildConfig.kt +++ b/buildSrc/src/main/kotlin/BuildConfig.kt @@ -4,7 +4,7 @@ object BuildConfig { const val compileSdkVersion = 34 const val targetSdk = 33 - val versionCode = 73 // Must be updated on every build + val versionCode = 74 // Must be updated on every build val version = Version( major = 1, minor = 0,