From 536b0ca34532f52677790cd8a18dca5cbf7ce1d1 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Thu, 23 May 2024 14:58:25 +0300 Subject: [PATCH 01/20] Implement export studypad --- .../android/control/backup/BackupControl.kt | 15 +- .../OldMonolithicAppDatabaseMigrations.kt | 8 +- .../activity/page/screen/SplitBibleArea.kt | 21 ++- .../bible/service/cloudsync/SyncUtilities.kt | 59 ++++-- .../net/bible/service/common/CommonUtils.kt | 14 ++ .../net/bible/service/db/DatabaseContainer.kt | 34 ++-- .../service/db/ExportAndImportStudypads.kt | 174 ++++++++++++++++++ app/src/main/res/menu/window_popup_menu.xml | 4 + app/src/main/res/values/strings.xml | 4 + 9 files changed, 281 insertions(+), 52 deletions(-) create mode 100644 app/src/main/java/net/bible/service/db/ExportAndImportStudypads.kt diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index c448d8aac3..0ea1049cbb 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -68,6 +68,7 @@ import net.bible.service.db.OLD_MONOLITHIC_DATABASE_NAME import net.bible.service.download.isPseudoBook import net.bible.service.cloudsync.CloudSync import net.bible.service.common.CommonUtils.determineFileType +import net.bible.service.common.CommonUtils.grantUriReadPermissions import net.bible.service.sword.dbFile import net.bible.service.sword.epub.epubDir import net.bible.service.sword.epub.isManuallyInstalledEpub @@ -471,18 +472,6 @@ object BackupControl { } - private fun grantUriReadPermissions(chooserIntent: Intent, uri: Uri) { - val resInfoList = if (Build.VERSION.SDK_INT >= 33) { - BibleApplication.application.packageManager.queryIntentActivities(chooserIntent, ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())) - } else { - BibleApplication.application.packageManager.queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY) - } - for (resolveInfo in resInfoList) { - val packageName = resolveInfo.activityInfo.packageName - BibleApplication.application.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) - } - } - suspend fun backupApp(callingActivity: ActivityBase) { internalDbBackupDir.mkdirs() @@ -740,7 +729,7 @@ object BackupControl { private var moduleDir: File = SharedConstants.modulesDir private lateinit var internalDbDir : File - private val internalDbBackupDir: File // copy of db is created in this dir when doing backups + val internalDbBackupDir: File // copy of db is created in this dir when doing backups get() { val file = File(SharedConstants.internalFilesDir, "/backup") file.mkdirs() diff --git a/app/src/main/java/net/bible/android/database/migrations/OldMonolithicAppDatabaseMigrations.kt b/app/src/main/java/net/bible/android/database/migrations/OldMonolithicAppDatabaseMigrations.kt index a30d7cd3bf..686f3f4ebf 100644 --- a/app/src/main/java/net/bible/android/database/migrations/OldMonolithicAppDatabaseMigrations.kt +++ b/app/src/main/java/net/bible/android/database/migrations/OldMonolithicAppDatabaseMigrations.kt @@ -289,8 +289,12 @@ fun getColumnNames(db: SupportSQLiteDatabase, tableName: String, schema: String? return columnNames } -fun joinColumnNames(columnNames: List): String { - return columnNames.joinToString(",", transform = { "`${it}`" }) +fun joinColumnNames(columnNames: List, prefix: String? = null): String { + if (prefix != null) { + return columnNames.joinToString(",", transform = { "$prefix.`${it}`" }) + } else { + return columnNames.joinToString(",", transform = { "`${it}`" }) + } } fun getColumnNamesJoined(db: SupportSQLiteDatabase, tableName: String, schema: String? = null): String { diff --git a/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt b/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt index e9cb9e4c3d..3667abe1d1 100644 --- a/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt +++ b/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt @@ -78,6 +78,7 @@ import net.bible.android.view.util.widget.AddNewWindowButtonWidget import net.bible.android.view.util.widget.WindowButtonWidget import net.bible.service.common.CommonUtils import net.bible.service.common.shortName +import net.bible.service.db.exportStudyPads import net.bible.service.device.ScreenSettings import net.bible.service.download.isStudyPad import net.bible.service.sword.BookAndKey @@ -718,8 +719,11 @@ class SplitBibleArea(private val mainBibleActivity: MainBibleActivity): FrameLay val textOptionsSubMenu = menu.findItem(R.id.textOptionsSubMenu).subMenu!! - val export = menu.findItem(R.id.exportHtml) - export.title = app.getString(R.string.export_fileformat, "HTML") + val exportHtml = menu.findItem(R.id.exportHtml) + exportHtml.title = app.getString(R.string.export_fileformat, "HTML") + + val exportStudypad = menu.findItem(R.id.exportStudypad) + exportStudypad.title = app.getString(R.string.export_fileformat, app.getString(R.string.studypad)) synchronized(BookName::class.java) { val oldValue = BookName.isFullBookName() @@ -956,9 +960,16 @@ class SplitBibleArea(private val mainBibleActivity: MainBibleActivity): FrameLay }, visible = window.isVisible && ( firstDoc is StudyPadDocument || - firstDoc is MultiFragmentDocument || - firstDoc is MyNotesDocument - ) + firstDoc is MultiFragmentDocument || + firstDoc is MyNotesDocument + ) + ) + R.id.exportStudypad -> CommandPreference({ _, _, _ -> + mainBibleActivity.lifecycleScope.launch { + exportStudyPads(listOf((firstDoc as StudyPadDocument).label), mainBibleActivity) + } + }, + visible = window.isVisible && (firstDoc is StudyPadDocument) ) else -> throw RuntimeException("Illegal menu item") } diff --git a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt index 2fb405b6ae..2b6285f794 100644 --- a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt +++ b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt @@ -34,7 +34,11 @@ const val TRIGGERS_DISABLED_KEY = "triggersDisabled" enum class SyncableDatabaseDefinition { BOOKMARKS, WORKSPACES, READINGPLANS; - class Table(val tableName: String, val idField1: String = "id", val idField2: String? = null) + class Table( + val tableName: String, + val idField1: String = "id", + val idField2: String? = null, + ) val contentDescription: Int get() = when(this) { READINGPLANS -> R.string.reading_plans_content BOOKMARKS -> R.string.bookmarks_contents @@ -43,24 +47,49 @@ enum class SyncableDatabaseDefinition { val tables get() = when(this) { BOOKMARKS -> listOf( - Table("Label"), - Table("BibleBookmark"), - Table("BibleBookmarkNotes", "bookmarkId"), - Table("BibleBookmarkToLabel", "bookmarkId", "labelId"), - Table("GenericBookmark"), - Table("GenericBookmarkNotes", "bookmarkId"), - Table("GenericBookmarkToLabel", "bookmarkId", "labelId"), - Table("StudyPadTextEntry"), - Table("StudyPadTextEntryText", "studyPadTextEntryId"), + Table( + tableName = "Label" + ), + Table( + tableName = "BibleBookmark" + ), + Table( + tableName = "BibleBookmarkNotes", + idField1 = "bookmarkId" + ), + Table( + tableName = "BibleBookmarkToLabel", + idField1 = "bookmarkId", + idField2 = "labelId" + ), + Table( + tableName = "GenericBookmark" + ), + Table( + tableName = "GenericBookmarkNotes", + idField1 = "bookmarkId" + ), + Table( + tableName = "GenericBookmarkToLabel", + idField1 = "bookmarkId", + idField2 = "labelId" + ), + Table( + tableName = "StudyPadTextEntry" + ), + Table( + tableName = "StudyPadTextEntryText", + idField1 = "studyPadTextEntryId" + ), ) WORKSPACES -> listOf( - Table("Workspace"), - Table("Window"), - Table("PageManager", "windowId"), + Table(tableName = "Workspace"), + Table(tableName = "Window"), + Table(tableName = "PageManager", idField1 = "windowId"), ) READINGPLANS -> listOf( - Table("ReadingPlan"), - Table("ReadingPlanStatus"), + Table(tableName = "ReadingPlan"), + Table(tableName = "ReadingPlanStatus"), ) } diff --git a/app/src/main/java/net/bible/service/common/CommonUtils.kt b/app/src/main/java/net/bible/service/common/CommonUtils.kt index 2b0f906b14..13e3eae6d0 100644 --- a/app/src/main/java/net/bible/service/common/CommonUtils.kt +++ b/app/src/main/java/net/bible/service/common/CommonUtils.kt @@ -100,6 +100,7 @@ import net.bible.android.control.backup.BackupControl import net.bible.android.control.event.ABEventBus import net.bible.android.control.event.ToastEvent import net.bible.android.control.page.OrdinalRange +import net.bible.android.control.page.StudyPadDocument import net.bible.android.control.page.window.WindowControl import net.bible.android.control.speak.SpeakControl import net.bible.android.control.versification.BibleTraverser @@ -1672,6 +1673,19 @@ object CommonUtils : CommonUtilsBase() { fun parseAndBibleReference(uri: String): BookAndKey? = parseAndBibleReference(Uri.parse(uri)) + + fun grantUriReadPermissions(chooserIntent: Intent, uri: Uri) { + val resInfoList = if (Build.VERSION.SDK_INT >= 33) { + BibleApplication.application.packageManager.queryIntentActivities(chooserIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())) + } else { + BibleApplication.application.packageManager.queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY) + } + for (resolveInfo in resInfoList) { + val packageName = resolveInfo.activityInfo.packageName + BibleApplication.application.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + } + } const val CALC_NOTIFICATION_CHANNEL = "calc-notifications" diff --git a/app/src/main/java/net/bible/service/db/DatabaseContainer.kt b/app/src/main/java/net/bible/service/db/DatabaseContainer.kt index 2f7f4712cb..3b5b60e5f5 100644 --- a/app/src/main/java/net/bible/service/db/DatabaseContainer.kt +++ b/app/src/main/java/net/bible/service/db/DatabaseContainer.kt @@ -55,7 +55,7 @@ import java.util.* const val OLD_MONOLITHIC_DATABASE_NAME = "andBibleDatabase.db" -const val TAG = "DbContainer" +private const val TAG = "DbContainer" val ALL_DB_FILENAMES = arrayOf( BookmarkDatabase.dbFileName, @@ -313,30 +313,30 @@ class DatabaseContainer { fun getDatabaseAccessorFactories(container: DatabaseContainer): List<() -> SyncableDatabaseAccessor<*>> = container.run { listOf( { SyncableDatabaseAccessor( - bookmarkDb, - { n -> getBookmarkDb(n) }, { resetBookmarkDb() }, - application.getDatabasePath(BookmarkDatabase.dbFileName), - SyncableDatabaseDefinition.BOOKMARKS, - { entries -> + localDb = bookmarkDb, + dbFactory = { n -> getBookmarkDb(n) }, _resetLocalDb = { resetBookmarkDb() }, + localDbFile = application.getDatabasePath(BookmarkDatabase.dbFileName), + category = SyncableDatabaseDefinition.BOOKMARKS, + _reactToUpdates = { entries -> ABEventBus.post(BookmarksUpdatedViaSyncEvent(entries)) }, ) }, { SyncableDatabaseAccessor( - workspaceDb, - { n -> getWorkspaceDb(n) }, { resetWorkspaceDb() }, - application.getDatabasePath(WorkspaceDatabase.dbFileName), - SyncableDatabaseDefinition.WORKSPACES, - { + localDb = workspaceDb, + dbFactory = { n -> getWorkspaceDb(n) }, _resetLocalDb = { resetWorkspaceDb() }, + localDbFile = application.getDatabasePath(WorkspaceDatabase.dbFileName), + category = SyncableDatabaseDefinition.WORKSPACES, + _reactToUpdates = { ABEventBus.post(WorkspacesUpdatedViaSyncEvent(it)) }, ) }, { SyncableDatabaseAccessor( - readingPlanDb, - { n -> getReadingPlanDb(n) }, - { resetReadingPlanDb() }, - application.getDatabasePath(ReadingPlanDatabase.dbFileName), - SyncableDatabaseDefinition.READINGPLANS, - { + localDb = readingPlanDb, + dbFactory = { n -> getReadingPlanDb(n) }, + _resetLocalDb = { resetReadingPlanDb() }, + localDbFile = application.getDatabasePath(ReadingPlanDatabase.dbFileName), + category = SyncableDatabaseDefinition.READINGPLANS, + _reactToUpdates = { ABEventBus.post(ReadingPlansUpdatedViaSyncEvent(it)) }, ) diff --git a/app/src/main/java/net/bible/service/db/ExportAndImportStudypads.kt b/app/src/main/java/net/bible/service/db/ExportAndImportStudypads.kt new file mode 100644 index 0000000000..847ff2c873 --- /dev/null +++ b/app/src/main/java/net/bible/service/db/ExportAndImportStudypads.kt @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2024 Martin Denham, Tuomas Airaksinen and the AndBible contributors. + * + * This file is part of AndBible: Bible Study (http://github.com/AndBible/and-bible). + * + * AndBible is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * AndBible is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AndBible. + * If not, see http://www.gnu.org/licenses/. + */ + +package net.bible.service.db + +import android.content.Intent +import androidx.core.content.FileProvider +import androidx.sqlite.db.SupportSQLiteDatabase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import net.bible.android.BibleApplication +import net.bible.android.activity.BuildConfig +import net.bible.android.activity.R +import net.bible.android.control.backup.BackupControl +import net.bible.android.control.backup.DATABASE_BACKUP_NAME +import net.bible.android.control.backup.ZIP_MIMETYPE +import net.bible.android.database.BookmarkDatabase +import net.bible.android.database.bookmarks.BookmarkEntities +import net.bible.android.database.migrations.getColumnNames +import net.bible.android.database.migrations.getColumnNamesJoined +import net.bible.android.database.migrations.joinColumnNames +import net.bible.android.view.activity.base.ActivityBase +import net.bible.service.common.CommonUtils +import net.bible.service.common.CommonUtils.grantUriReadPermissions +import java.io.BufferedInputStream +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +private const val TAG = "ExportStudyPad" + +private fun copyStudyPad( + label: BookmarkEntities.Label, + db: SupportSQLiteDatabase, + sourceSchema: String = "main", + targetSchema: String = "export" +) = db.run { + val labelCols = getColumnNamesJoined(db, "Label", targetSchema) + val bibleBookmarkCols = getColumnNamesJoined(db, "BibleBookmark", targetSchema) + val bibleBookmarkToLabelCols = getColumnNamesJoined(db, "BibleBookmarkToLabel", targetSchema) + val bibleBookmarkNotesCols = getColumnNames(db, "BibleBookmarkNotes", targetSchema) + + val genericBookmarkCols = getColumnNamesJoined(db, "GenericBookmark", targetSchema) + val genericBookmarkToLabelCols = getColumnNamesJoined(db, "GenericBookmarkToLabel", targetSchema) + val genericBookmarkNotesCols = getColumnNames(db, "GenericBookmarkNotes", targetSchema) + + val studyPadTextEntryCols = getColumnNamesJoined(db, "StudyPadTextEntry", targetSchema) + val studyPadTextEntryTextCols = getColumnNamesJoined(db, "StudyPadTextEntryText", targetSchema) + + val labelIdHex = label.id.toString().replace("-", "") + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.Label ($labelCols) + SELECT $labelCols FROM $sourceSchema.Label WHERE id = x'$labelIdHex' + """.trimIndent()) + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.BibleBookmark ($bibleBookmarkCols) + SELECT $bibleBookmarkCols FROM $sourceSchema.BibleBookmark bb + INNER JOIN $sourceSchema.BibleBookmarkToLabel bbl ON bb.id = bbl.bookmarkId + WHERE bbl.labelId = x'$labelIdHex' + """.trimIndent()) + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.BibleBookmarkNotes (${joinColumnNames(bibleBookmarkNotesCols)}) + SELECT ${joinColumnNames(bibleBookmarkNotesCols, "bb")} FROM $sourceSchema.BibleBookmarkNotes bb + INNER JOIN $sourceSchema.BibleBookmarkToLabel bbl ON bb.bookmarkId = bbl.bookmarkId + WHERE bbl.labelId = x'$labelIdHex' + """.trimIndent()) + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.BibleBookmarkToLabel ($bibleBookmarkToLabelCols) + SELECT $bibleBookmarkToLabelCols FROM $sourceSchema.BibleBookmarkToLabel + WHERE labelId = x'$labelIdHex' + """.trimIndent()) + + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.GenericBookmark ($genericBookmarkCols) + SELECT $genericBookmarkCols FROM $sourceSchema.GenericBookmark bb + INNER JOIN $sourceSchema.GenericBookmarkToLabel bbl ON bb.id = bbl.bookmarkId + WHERE bbl.labelId = x'$labelIdHex' + """.trimIndent()) + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.GenericBookmarkNotes (${joinColumnNames(genericBookmarkNotesCols)}) + SELECT ${joinColumnNames(genericBookmarkNotesCols, "bb")} FROM $sourceSchema.GenericBookmarkNotes bb + INNER JOIN $sourceSchema.GenericBookmarkToLabel bbl ON bb.bookmarkId = bbl.bookmarkId + WHERE bbl.labelId = x'$labelIdHex' + """.trimIndent()) + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.GenericBookmarkToLabel ($genericBookmarkToLabelCols) + SELECT $genericBookmarkToLabelCols FROM $sourceSchema.GenericBookmarkToLabel + WHERE labelId = x'$labelIdHex' + """.trimIndent()) + + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.StudyPadTextEntry ($studyPadTextEntryCols) + SELECT $studyPadTextEntryCols FROM $sourceSchema.StudyPadTextEntry te + WHERE te.labelId = x'$labelIdHex' + """.trimIndent()) + execSQL(""" + INSERT OR IGNORE INTO $targetSchema.StudyPadTextEntryText ($studyPadTextEntryTextCols) + SELECT $studyPadTextEntryTextCols FROM $sourceSchema.StudyPadTextEntryText tet + INNER JOIN $sourceSchema.StudyPadTextEntry te ON tet.studyPadTextEntryId = te.id + WHERE te.labelId = x'$labelIdHex' + """.trimIndent()) +} + +suspend fun exportStudyPads(labels: List, activity: ActivityBase) = withContext(Dispatchers.IO) { + val exportDbFile = CommonUtils.tmpFile + val exportDb = DatabaseContainer.instance.getBookmarkDb(exportDbFile.absolutePath) + exportDb.openHelper.writableDatabase.use {} + DatabaseContainer.instance.bookmarkDb.openHelper.writableDatabase.run { + execSQL("ATTACH DATABASE '${exportDbFile.absolutePath}' AS export") + beginTransaction() + for (label in labels) { + copyStudyPad(label, this) + } + setTransactionSuccessful() + endTransaction() + execSQL("DETACH DATABASE export") + } + + val filename = if (labels.size > 1) "StudyPads.abdb" else labels.first().name + ".adbd" + val zipFile = File(BackupControl.internalDbBackupDir, filename) + ZipOutputStream(FileOutputStream(zipFile)).use { outFile -> + FileInputStream(exportDbFile).use { inFile -> + BufferedInputStream(inFile).use { origin -> + val entry = ZipEntry("db/${BookmarkDatabase.dbFileName}") + outFile.putNextEntry(entry) + origin.copyTo(outFile) + } + } + } + exportDbFile.delete() + sendFile(filename, zipFile, activity) + zipFile.delete() +} + +suspend fun sendFile(filename: String, file: File, activity: ActivityBase) { + val subject = activity.getString(R.string.exported_studypads_subject) + val message = activity.getString(R.string.exported_studypads_message, CommonUtils.applicationNameMedium) + val uri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".provider", file) + val shareIntent = Intent(Intent.ACTION_SEND).apply { + putExtra(Intent.EXTRA_STREAM, uri) + putExtra(Intent.EXTRA_SUBJECT, subject) + putExtra(Intent.EXTRA_TEXT, message) + type = ZIP_MIMETYPE + } + val saveIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = ZIP_MIMETYPE + putExtra(Intent.EXTRA_TITLE, filename) + } + + val chooserIntent = Intent.createChooser(shareIntent, activity.getString(R.string.send_backup_file)) + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(saveIntent)) + grantUriReadPermissions(chooserIntent, uri) + activity.awaitIntent(chooserIntent).data?.data?.let { + val out = BibleApplication.application.contentResolver.openOutputStream(it)!! + FileInputStream(file).copyTo(out) + } +} diff --git a/app/src/main/res/menu/window_popup_menu.xml b/app/src/main/res/menu/window_popup_menu.xml index 9be2ad00e4..5d36826597 100644 --- a/app/src/main/res/menu/window_popup_menu.xml +++ b/app/src/main/res/menu/window_popup_menu.xml @@ -67,6 +67,10 @@ android:title="" android:icon="@drawable/file_export" /> + %s database backup Your %s database containing bookmarks, notes, reading plans and workspaces is attached + + Exported StudyPad(s) + Your %s database containing bookmarks, notes related to selected StudyPads is attached Restoring database was unsuccessful. Perhaps you gave invalid database file. Restore Documents from… Send a backup file @@ -890,6 +893,7 @@ User-editable study pads with custom text and bookmarks User-editable notes attached to bookmarks My Notes + Study Pad Study Pads Multiple references Multiple From 582f57d76da77a7abb7f2f2f3262244c022a4ff2 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Thu, 23 May 2024 16:08:51 +0300 Subject: [PATCH 02/20] Refactoring --- .../android/control/backup/BackupControl.kt | 7 ++-- .../view/activity/settings/SyncSettings.kt | 4 +-- .../net/bible/service/cloudsync/CloudSync.kt | 10 +++--- .../bible/service/cloudsync/SyncUtilities.kt | 4 +-- .../net/bible/service/common/CommonUtils.kt | 3 +- ...dImportStudypads.kt => ExportStudyPads.kt} | 36 +++++++++++-------- 6 files changed, 33 insertions(+), 31 deletions(-) rename app/src/main/java/net/bible/service/db/{ExportAndImportStudypads.kt => ExportStudyPads.kt} (91%) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index 0ea1049cbb..62d0db5673 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -22,11 +22,8 @@ import android.app.AlertDialog import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager -import android.content.pm.PackageManager.ResolveInfoFlags import io.requery.android.database.sqlite.SQLiteDatabase import android.net.Uri -import android.os.Build import android.os.Bundle import android.util.Log import android.view.MenuItem @@ -602,7 +599,7 @@ object BackupControl { private suspend fun beforeRestore() { if(DatabaseContainer.ready && CloudSync.signedIn) { for (it in DatabaseContainer.databaseAccessors) { - it.category.enabled = false + it.category.syncEnabled = false } ABEventBus.post(ToastEvent(R.string.disabling_sync)) CloudSync.waitUntilFinished() @@ -612,7 +609,7 @@ object BackupControl { private suspend fun afterRestore(selection: List? = null) { for (it in DatabaseContainer.databaseAccessors) { - it.category.enabled = false + it.category.syncEnabled = false } for(s in selection?: ALL_DB_FILENAMES.toList()) { val db: SyncableRoomDatabase? = when(s) { diff --git a/app/src/main/java/net/bible/android/view/activity/settings/SyncSettings.kt b/app/src/main/java/net/bible/android/view/activity/settings/SyncSettings.kt index 37d41944da..f08dd2cfb5 100644 --- a/app/src/main/java/net/bible/android/view/activity/settings/SyncSettings.kt +++ b/app/src/main/java/net/bible/android/view/activity/settings/SyncSettings.kt @@ -63,13 +63,13 @@ class SyncSettingsFragment: PreferenceFragmentCompat() { private fun setupDrivePref(pref: SwitchPreferenceCompat) { val category = SyncableDatabaseDefinition.nameToCategory[pref.key.split("_")[1].uppercase()]!! pref.setOnPreferenceClickListener { - if(category.enabled) { + if(category.syncEnabled) { lifecycleScope.launch { hourglass.show(R.string.synchronizing) if (!CloudSync.signedIn) { CloudSync.signIn(activity as ActivityBase) } - if (CloudSync.signedIn && category.enabled) { + if (CloudSync.signedIn && category.syncEnabled) { CloudSync.waitUntilFinished() CloudSync.start() CloudSync.waitUntilFinished() diff --git a/app/src/main/java/net/bible/service/cloudsync/CloudSync.kt b/app/src/main/java/net/bible/service/cloudsync/CloudSync.kt index 6009896574..242e248025 100644 --- a/app/src/main/java/net/bible/service/cloudsync/CloudSync.kt +++ b/app/src/main/java/net/bible/service/cloudsync/CloudSync.kt @@ -114,7 +114,7 @@ object CloudSync { DatabaseContainer.databaseAccessorFactories.asyncMap { val dbDef = it.invoke() val category = dbDef.category - category.enabled = false + category.syncEnabled = false dbDef.dao.clearSyncStatus() dbDef.dao.clearSyncConfiguration() } @@ -188,7 +188,7 @@ object CloudSync { } } if (initialOperation == null) { - dbDef.category.enabled = false + dbDef.category.syncEnabled = false throw CancelStartedSync() } else { dbDef.dao.setConfig(SYNC_FOLDER_FILE_ID_KEY, preliminarySyncFolderId!!) @@ -279,7 +279,7 @@ object CloudSync { tmpFile.delete() val activity = CurrentActivityHolder.currentActivity ?: throw CancelStartedSync() Dialogs.showMsg2(activity, cantFetchString(dbDef.category.contentDescription)) - dbDef.category.enabled = false + dbDef.category.syncEnabled = false Log.e(TAG, "Initial db version is newer than this app version: $initialDbVersion > ${dbDef.version}") throw CancelStartedSync() } else { @@ -357,7 +357,7 @@ object CloudSync { DatabaseContainer.databaseAccessorFactories.asyncMap { val dbDef = it.invoke() - if(!dbDef.category.enabled) return@asyncMap + if(!dbDef.category.syncEnabled) return@asyncMap if(dbDef.dao.getLong("disabledForVersion") == dbDef.version.toLong()) return@asyncMap try { initializeSync(dbDef) @@ -523,7 +523,7 @@ object CloudSync { suspend fun hasChanges(): Boolean = DatabaseContainer.databaseAccessorFactories.asyncMap { val dbDef = it.invoke() - dbDef.category.enabled && dbDef.hasChanges + dbDef.category.syncEnabled && dbDef.hasChanges }.any { it } suspend fun bytesUsed(): Long = diff --git a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt index 2b6285f794..70732d1463 100644 --- a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt +++ b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt @@ -93,12 +93,12 @@ enum class SyncableDatabaseDefinition { ) } - var enabled + var syncEnabled get() = CommonUtils.settings.getBoolean("gdrive_"+ name.lowercase(), false) set(value) = CommonUtils.settings.setBoolean("gdrive_"+name.lowercase(), value) private val accessor get() = DatabaseContainer.databaseAccessorsByCategory[this]!! - val lastSynchronized get() = if(!enabled) null else accessor.dao.getLong(LAST_SYNCHRONIZED_KEY) + val lastSynchronized get() = if(!syncEnabled) null else accessor.dao.getLong(LAST_SYNCHRONIZED_KEY) companion object { val ALL = arrayOf(BOOKMARKS, WORKSPACES, READINGPLANS) diff --git a/app/src/main/java/net/bible/service/common/CommonUtils.kt b/app/src/main/java/net/bible/service/common/CommonUtils.kt index 13e3eae6d0..062f962541 100644 --- a/app/src/main/java/net/bible/service/common/CommonUtils.kt +++ b/app/src/main/java/net/bible/service/common/CommonUtils.kt @@ -100,7 +100,6 @@ import net.bible.android.control.backup.BackupControl import net.bible.android.control.event.ABEventBus import net.bible.android.control.event.ToastEvent import net.bible.android.control.page.OrdinalRange -import net.bible.android.control.page.StudyPadDocument import net.bible.android.control.page.window.WindowControl import net.bible.android.control.speak.SpeakControl import net.bible.android.control.versification.BibleTraverser @@ -1506,7 +1505,7 @@ object CommonUtils : CommonUtilsBase() { val isCloudSyncEnabled: Boolean get () = if(!isCloudSyncAvailable) false - else SyncableDatabaseDefinition.ALL.any { it.enabled } + else SyncableDatabaseDefinition.ALL.any { it.syncEnabled } val isDiscrete get() = BuildVariant.Appearance.isDiscrete || realSharedPreferences.getBoolean("discrete_mode", false) val showCalculator get() = BuildVariant.Appearance.isDiscrete || realSharedPreferences.getBoolean("show_calculator", false) diff --git a/app/src/main/java/net/bible/service/db/ExportAndImportStudypads.kt b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt similarity index 91% rename from app/src/main/java/net/bible/service/db/ExportAndImportStudypads.kt rename to app/src/main/java/net/bible/service/db/ExportStudyPads.kt index 847ff2c873..b8ed305d3c 100644 --- a/app/src/main/java/net/bible/service/db/ExportAndImportStudypads.kt +++ b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt @@ -26,7 +26,6 @@ import net.bible.android.BibleApplication import net.bible.android.activity.BuildConfig import net.bible.android.activity.R import net.bible.android.control.backup.BackupControl -import net.bible.android.control.backup.DATABASE_BACKUP_NAME import net.bible.android.control.backup.ZIP_MIMETYPE import net.bible.android.database.BookmarkDatabase import net.bible.android.database.bookmarks.BookmarkEntities @@ -46,11 +45,11 @@ import java.util.zip.ZipOutputStream private const val TAG = "ExportStudyPad" private fun copyStudyPad( - label: BookmarkEntities.Label, db: SupportSQLiteDatabase, - sourceSchema: String = "main", - targetSchema: String = "export" + label: BookmarkEntities.Label, ) = db.run { + val sourceSchema: String = "main" + val targetSchema: String = "export" val labelCols = getColumnNamesJoined(db, "Label", targetSchema) val bibleBookmarkCols = getColumnNamesJoined(db, "BibleBookmark", targetSchema) val bibleBookmarkToLabelCols = getColumnNamesJoined(db, "BibleBookmarkToLabel", targetSchema) @@ -63,57 +62,64 @@ private fun copyStudyPad( val studyPadTextEntryCols = getColumnNamesJoined(db, "StudyPadTextEntry", targetSchema) val studyPadTextEntryTextCols = getColumnNamesJoined(db, "StudyPadTextEntryText", targetSchema) - val labelIdHex = label.id.toString().replace("-", "") + fun where(column: String): String { + return run { + val labelIdHex = label.id.toString().replace("-", "") + "WHERE $column = x'$labelIdHex'" + } + } + execSQL(""" INSERT OR IGNORE INTO $targetSchema.Label ($labelCols) - SELECT $labelCols FROM $sourceSchema.Label WHERE id = x'$labelIdHex' + SELECT $labelCols FROM $sourceSchema.Label + ${where("id")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.BibleBookmark ($bibleBookmarkCols) SELECT $bibleBookmarkCols FROM $sourceSchema.BibleBookmark bb INNER JOIN $sourceSchema.BibleBookmarkToLabel bbl ON bb.id = bbl.bookmarkId - WHERE bbl.labelId = x'$labelIdHex' + ${where("bbl.labelId")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.BibleBookmarkNotes (${joinColumnNames(bibleBookmarkNotesCols)}) SELECT ${joinColumnNames(bibleBookmarkNotesCols, "bb")} FROM $sourceSchema.BibleBookmarkNotes bb INNER JOIN $sourceSchema.BibleBookmarkToLabel bbl ON bb.bookmarkId = bbl.bookmarkId - WHERE bbl.labelId = x'$labelIdHex' + ${where("bbl.labelId")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.BibleBookmarkToLabel ($bibleBookmarkToLabelCols) SELECT $bibleBookmarkToLabelCols FROM $sourceSchema.BibleBookmarkToLabel - WHERE labelId = x'$labelIdHex' + ${where("labelId")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.GenericBookmark ($genericBookmarkCols) SELECT $genericBookmarkCols FROM $sourceSchema.GenericBookmark bb INNER JOIN $sourceSchema.GenericBookmarkToLabel bbl ON bb.id = bbl.bookmarkId - WHERE bbl.labelId = x'$labelIdHex' + ${where("bbl.labelId")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.GenericBookmarkNotes (${joinColumnNames(genericBookmarkNotesCols)}) SELECT ${joinColumnNames(genericBookmarkNotesCols, "bb")} FROM $sourceSchema.GenericBookmarkNotes bb INNER JOIN $sourceSchema.GenericBookmarkToLabel bbl ON bb.bookmarkId = bbl.bookmarkId - WHERE bbl.labelId = x'$labelIdHex' + ${where("bbl.labelId")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.GenericBookmarkToLabel ($genericBookmarkToLabelCols) SELECT $genericBookmarkToLabelCols FROM $sourceSchema.GenericBookmarkToLabel - WHERE labelId = x'$labelIdHex' + ${where("labelId")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.StudyPadTextEntry ($studyPadTextEntryCols) SELECT $studyPadTextEntryCols FROM $sourceSchema.StudyPadTextEntry te - WHERE te.labelId = x'$labelIdHex' + ${where("te.labelId")} """.trimIndent()) execSQL(""" INSERT OR IGNORE INTO $targetSchema.StudyPadTextEntryText ($studyPadTextEntryTextCols) SELECT $studyPadTextEntryTextCols FROM $sourceSchema.StudyPadTextEntryText tet INNER JOIN $sourceSchema.StudyPadTextEntry te ON tet.studyPadTextEntryId = te.id - WHERE te.labelId = x'$labelIdHex' + ${where("te.labelId")} """.trimIndent()) } @@ -125,7 +131,7 @@ suspend fun exportStudyPads(labels: List, activity: Acti execSQL("ATTACH DATABASE '${exportDbFile.absolutePath}' AS export") beginTransaction() for (label in labels) { - copyStudyPad(label, this) + copyStudyPad(this, label) } setTransactionSuccessful() endTransaction() From fd53e14e4a0579766555da7a1735b8e08b579ea1 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Thu, 23 May 2024 16:23:18 +0300 Subject: [PATCH 03/20] Implement merging / importing database file (Closes #3259) --- .../android/control/backup/BackupControl.kt | 43 +++++++++++--- .../bible/service/cloudsync/SyncUtilities.kt | 11 +++- .../net/bible/service/db/DatabaseContainer.kt | 2 + .../java/net/bible/service/db/ImportDb.kt | 57 +++++++++++++++++++ app/src/main/res/layout/backup_view.xml | 4 +- app/src/main/res/values/strings.xml | 5 ++ 6 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/net/bible/service/db/ImportDb.kt diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index 62d0db5673..9741670957 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -64,8 +64,10 @@ import net.bible.service.db.DatabaseContainer.Companion.maxDatabaseVersion import net.bible.service.db.OLD_MONOLITHIC_DATABASE_NAME import net.bible.service.download.isPseudoBook import net.bible.service.cloudsync.CloudSync +import net.bible.service.cloudsync.SyncableDatabaseDefinition import net.bible.service.common.CommonUtils.determineFileType import net.bible.service.common.CommonUtils.grantUriReadPermissions +import net.bible.service.db.importDatabaseFile import net.bible.service.sword.dbFile import net.bible.service.sword.epub.epubDir import net.bible.service.sword.epub.isManuallyInstalledEpub @@ -662,17 +664,28 @@ object BackupControl { } hourglass.show() beforeRestore() - DatabaseContainer.reset() for (fileName in selection) { + val category = SyncableDatabaseDefinition.filenameToCategory[fileName] + val restore = + if (category != null) + askIfRestoreOrImport(category, activity) + else true + val f = File(unzipFolder, "db/${fileName}") - Log.i(TAG, "Restoring $fileName") - val targetFilePath = activity.getDatabasePath(fileName).path - val targetFile = File(targetFilePath) - f.copyTo(targetFile, overwrite = true) - File("$targetFilePath-journal").delete() - File("$targetFilePath-shm").delete() - File("$targetFilePath-wal").delete() + if (restore) { + Log.i(TAG, "Restoring $fileName") + DatabaseContainer.instance.dbByFilename[fileName]?.close() + val targetFilePath = activity.getDatabasePath(fileName).path + val targetFile = File(targetFilePath) + f.copyTo(targetFile, overwrite = true) + File("$targetFilePath-journal").delete() + File("$targetFilePath-shm").delete() + File("$targetFilePath-wal").delete() + } else { + importDatabaseFile(category!!, f) + } } + DatabaseContainer.reset() selection } if (DatabaseContainer.ready) { @@ -686,6 +699,20 @@ object BackupControl { true } + private suspend fun askIfRestoreOrImport(category: SyncableDatabaseDefinition, context: ActivityBase): Boolean = withContext(Dispatchers.Main) { + suspendCoroutine { + val message = + context.getString(R.string.ask_restore_or_import, context.getString(category.contentDescription)) + AlertDialog.Builder(context) + .setTitle(R.string.backup_restore2) + .setMessage(message) + .setPositiveButton(R.string.restore) { _, _ -> it.resume(true) } + .setNegativeButton(R.string.import2) { _, _ -> it.resume(false) } + .setOnCancelListener { _ -> it.resume(false) } + .show() + } + } + private suspend fun restoreOldMonolithicDatabaseFromFileInputStreamWithUI( activity: ActivityBase, inputStream: InputStream diff --git a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt index 70732d1463..660d8254a5 100644 --- a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt +++ b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt @@ -20,8 +20,11 @@ package net.bible.service.cloudsync import android.util.Log import androidx.sqlite.db.SupportSQLiteDatabase import net.bible.android.activity.R +import net.bible.android.database.BookmarkDatabase import net.bible.android.database.LogEntry +import net.bible.android.database.ReadingPlanDatabase import net.bible.android.database.SyncableRoomDatabase +import net.bible.android.database.WorkspaceDatabase import net.bible.android.database.migrations.getColumnNames import net.bible.android.database.migrations.getColumnNamesJoined import net.bible.service.common.CommonUtils @@ -45,6 +48,11 @@ enum class SyncableDatabaseDefinition { WORKSPACES -> R.string.workspaces_contents } + val filename get() = when(this) { + BOOKMARKS -> BookmarkDatabase.dbFileName + READINGPLANS ->ReadingPlanDatabase.dbFileName + WORKSPACES -> WorkspaceDatabase.dbFileName + } val tables get() = when(this) { BOOKMARKS -> listOf( Table( @@ -97,12 +105,13 @@ enum class SyncableDatabaseDefinition { get() = CommonUtils.settings.getBoolean("gdrive_"+ name.lowercase(), false) set(value) = CommonUtils.settings.setBoolean("gdrive_"+name.lowercase(), value) - private val accessor get() = DatabaseContainer.databaseAccessorsByCategory[this]!! + val accessor get() = DatabaseContainer.databaseAccessorsByCategory[this]!! val lastSynchronized get() = if(!syncEnabled) null else accessor.dao.getLong(LAST_SYNCHRONIZED_KEY) companion object { val ALL = arrayOf(BOOKMARKS, WORKSPACES, READINGPLANS) val nameToCategory = ALL.associateBy { it.name } + val filenameToCategory = ALL.associateBy { it.filename } } } diff --git a/app/src/main/java/net/bible/service/db/DatabaseContainer.kt b/app/src/main/java/net/bible/service/db/DatabaseContainer.kt index 3b5b60e5f5..4b389f073b 100644 --- a/app/src/main/java/net/bible/service/db/DatabaseContainer.kt +++ b/app/src/main/java/net/bible/service/db/DatabaseContainer.kt @@ -253,6 +253,8 @@ class DatabaseContainer { private val backedUpDatabases = arrayOf(bookmarkDb, readingPlanDb, workspaceDb, repoDb, settingsDb) private val allDatabases = arrayOf(*backedUpDatabases, downloadDocumentsDb, chooseDocumentsDb) + val dbByFilename = allDatabases.associateBy { it.openHelper.databaseName } + internal fun sync() = allDatabases.forEach { it.openHelper.writableDatabase // we are not using WAL mode any more, but it does not hurt either. Just in case we switch back to WAL. diff --git a/app/src/main/java/net/bible/service/db/ImportDb.kt b/app/src/main/java/net/bible/service/db/ImportDb.kt new file mode 100644 index 0000000000..7f12d25bda --- /dev/null +++ b/app/src/main/java/net/bible/service/db/ImportDb.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Martin Denham, Tuomas Airaksinen and the AndBible contributors. + * + * This file is part of AndBible: Bible Study (http://github.com/AndBible/and-bible). + * + * AndBible is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * AndBible is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AndBible. + * If not, see http://www.gnu.org/licenses/. + */ + +package net.bible.service.db + +import android.util.Log +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import net.bible.android.database.migrations.getColumnNamesJoined +import net.bible.service.cloudsync.SyncableDatabaseDefinition +import java.io.File +import java.lang.Exception + +private const val TAG = "ImportDb" + +suspend fun importDatabaseFile(category: SyncableDatabaseDefinition, dbFile: File) = withContext(Dispatchers.IO) { + val dbDef = category.accessor + val importDbFile = dbDef.dbFactory(dbFile.absolutePath) + importDbFile.openHelper.writableDatabase.use {} + dbDef.writableDb.run { + execSQL("ATTACH DATABASE '${dbFile.absolutePath}' AS import") + execSQL("PRAGMA foreign_keys=OFF;") + beginTransaction() + try { + for (tableDef in dbDef.tableDefinitions) { + val table = tableDef.tableName + val cols = getColumnNamesJoined(this, table) + execSQL(""" + INSERT OR IGNORE INTO $table ($cols) + SELECT $cols FROM import.$table + """.trimIndent()) + } + setTransactionSuccessful() + } catch (e: Exception) { + Log.e(TAG, "Error occurred in importDatabaseFile", e) + throw e + } finally { + endTransaction() + execSQL("PRAGMA foreign_keys=ON;") + execSQL("DETACH DATABASE import") + } + } +} diff --git a/app/src/main/res/layout/backup_view.xml b/app/src/main/res/layout/backup_view.xml index a0a124a4da..333355a7d5 100644 --- a/app/src/main/res/layout/backup_view.xml +++ b/app/src/main/res/layout/backup_view.xml @@ -90,7 +90,7 @@ @@ -131,7 +131,7 @@ android:layout_marginTop="10dp" android:layout_marginLeft="50dp" android:layout_marginRight="50dp" - android:text="@string/backup_restore_from" /> + android:text="@string/backup_restore_from2" /> Bibles, commentaries, dictionaries, maps etc. Backup to… Restore from… + Restore or Import from… Restore + Restore or Import What do you want to restore? @@ -1217,4 +1219,7 @@ Don\'t show this message again + Do you want to restore (remove existing and read selected database) or import (import selected database into existing database) %s? + Restore + Import From 7ebf0bad45fc5e7b1f1fadc3a091721d1a74f44f Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Thu, 23 May 2024 17:54:54 +0300 Subject: [PATCH 04/20] Improve strings --- .../java/net/bible/android/control/backup/BackupControl.kt | 6 ++++-- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index 9741670957..198c9c9379 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -670,6 +670,7 @@ object BackupControl { if (category != null) askIfRestoreOrImport(category, activity) else true + if (restore == null) continue val f = File(unzipFolder, "db/${fileName}") if (restore) { @@ -699,13 +700,14 @@ object BackupControl { true } - private suspend fun askIfRestoreOrImport(category: SyncableDatabaseDefinition, context: ActivityBase): Boolean = withContext(Dispatchers.Main) { + private suspend fun askIfRestoreOrImport(category: SyncableDatabaseDefinition, context: ActivityBase): Boolean? = withContext(Dispatchers.Main) { suspendCoroutine { val message = context.getString(R.string.ask_restore_or_import, context.getString(category.contentDescription)) AlertDialog.Builder(context) - .setTitle(R.string.backup_restore2) + .setTitle(category.contentDescription) .setMessage(message) + .setNeutralButton(R.string.cancel) {_, _ -> it.resume(null) } .setPositiveButton(R.string.restore) { _, _ -> it.resume(true) } .setNegativeButton(R.string.import2) { _, _ -> it.resume(false) } .setOnCancelListener { _ -> it.resume(false) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0c36ce4c38..34b061a142 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1219,7 +1219,7 @@ Don\'t show this message again - Do you want to restore (remove existing and read selected database) or import (import selected database into existing database) %s? + Database for %s can be either restored (i.e. existing database is removed and selected database is loaded) or imported (selected database is imported into existing database). What do you want to do? Restore Import From 9e1b163c344373a3fdd7df8120b7d306e9a70781 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Thu, 23 May 2024 18:08:52 +0300 Subject: [PATCH 05/20] Add export function to label editor --- .../android/view/activity/bookmark/LabelEditActivity.kt | 3 +++ .../android/view/activity/page/screen/SplitBibleArea.kt | 2 +- app/src/main/java/net/bible/service/db/ExportStudyPads.kt | 2 +- app/src/main/res/menu/edit_label_options_menu.xml | 5 +++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt b/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt index 881c36d138..530bd4fe1c 100644 --- a/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt +++ b/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt @@ -43,6 +43,7 @@ import net.bible.android.view.activity.base.ActivityBase import net.bible.service.common.CommonUtils.getTintedDrawable import net.bible.service.common.CommonUtils.json import net.bible.service.common.displayName +import net.bible.service.db.exportStudyPads import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -72,6 +73,7 @@ class LabelEditActivity: ActivityBase(), ColorPickerDialogListener { override fun onCreateOptionsMenu(menu: Menu): Boolean { Log.i(TAG, "onCreateOptionsMenu") menuInflater.inflate(R.menu.edit_label_options_menu, menu) + menu.findItem(R.id.share).title = getString(R.string.export_fileformat, getString(R.string.studypad)) if(data.label.isSpecialLabel) { menu.findItem(R.id.removeLabel).isVisible = false } @@ -83,6 +85,7 @@ class LabelEditActivity: ActivityBase(), ColorPickerDialogListener { var isHandled = true when(item.itemId){ R.id.removeLabel -> remove() + R.id.share -> lifecycleScope.launch { exportStudyPads(this@LabelEditActivity, data.label) } android.R.id.home -> saveAndExit() else -> isHandled = false } diff --git a/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt b/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt index 3667abe1d1..6ef0b2d75d 100644 --- a/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt +++ b/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt @@ -966,7 +966,7 @@ class SplitBibleArea(private val mainBibleActivity: MainBibleActivity): FrameLay ) R.id.exportStudypad -> CommandPreference({ _, _, _ -> mainBibleActivity.lifecycleScope.launch { - exportStudyPads(listOf((firstDoc as StudyPadDocument).label), mainBibleActivity) + exportStudyPads(mainBibleActivity, (firstDoc as StudyPadDocument).label) } }, visible = window.isVisible && (firstDoc is StudyPadDocument) diff --git a/app/src/main/java/net/bible/service/db/ExportStudyPads.kt b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt index b8ed305d3c..9930b0679b 100644 --- a/app/src/main/java/net/bible/service/db/ExportStudyPads.kt +++ b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt @@ -123,7 +123,7 @@ private fun copyStudyPad( """.trimIndent()) } -suspend fun exportStudyPads(labels: List, activity: ActivityBase) = withContext(Dispatchers.IO) { +suspend fun exportStudyPads(activity: ActivityBase, vararg labels: BookmarkEntities.Label) = withContext(Dispatchers.IO) { val exportDbFile = CommonUtils.tmpFile val exportDb = DatabaseContainer.instance.getBookmarkDb(exportDbFile.absolutePath) exportDb.openHelper.writableDatabase.use {} diff --git a/app/src/main/res/menu/edit_label_options_menu.xml b/app/src/main/res/menu/edit_label_options_menu.xml index b98e468abf..4cb07c8560 100644 --- a/app/src/main/res/menu/edit_label_options_menu.xml +++ b/app/src/main/res/menu/edit_label_options_menu.xml @@ -23,4 +23,9 @@ android:icon="@drawable/ic_delete_24dp" app:showAsAction="ifRoom" /> + From fea75a8c4adbad1e7a55890db25f6ea72794d44e Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 16:50:38 +0300 Subject: [PATCH 06/20] Rename "Export as StudyPads" to "Export" in LabelEditActivity --- .../bible/android/view/activity/bookmark/LabelEditActivity.kt | 1 - .../net/bible/android/view/activity/bookmark/ManageLabels.kt | 2 ++ app/src/main/res/menu/edit_label_options_menu.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt b/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt index 530bd4fe1c..dee8e8b2da 100644 --- a/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt +++ b/app/src/main/java/net/bible/android/view/activity/bookmark/LabelEditActivity.kt @@ -73,7 +73,6 @@ class LabelEditActivity: ActivityBase(), ColorPickerDialogListener { override fun onCreateOptionsMenu(menu: Menu): Boolean { Log.i(TAG, "onCreateOptionsMenu") menuInflater.inflate(R.menu.edit_label_options_menu, menu) - menu.findItem(R.id.share).title = getString(R.string.export_fileformat, getString(R.string.studypad)) if(data.label.isSpecialLabel) { menu.findItem(R.id.removeLabel).isVisible = false } diff --git a/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt b/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt index 9e74a41711..96a52cbe67 100644 --- a/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt +++ b/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt @@ -75,6 +75,7 @@ import net.bible.service.common.CommonUtils.convertDipsToPx import net.bible.service.common.CommonUtils.getResourceColor import net.bible.service.common.displayName import net.bible.service.db.BookmarksUpdatedViaSyncEvent +import net.bible.service.db.importDatabaseFile import kotlin.collections.ArrayList import net.bible.service.device.ScreenSettings import java.util.regex.PatternSyntaxException @@ -329,6 +330,7 @@ class ManageLabels : ListActivityBase() { R.id.help -> help() R.id.newLabel -> newLabel() R.id.resetButton -> reset() + R.id.import_studypads -> importDatabaseFile() R.id.reOrder -> updateLabelList(rePopulate = true, reOrder = true) android.R.id.home -> saveAndExit() else -> isHandled = false diff --git a/app/src/main/res/menu/edit_label_options_menu.xml b/app/src/main/res/menu/edit_label_options_menu.xml index 4cb07c8560..327ceb90f0 100644 --- a/app/src/main/res/menu/edit_label_options_menu.xml +++ b/app/src/main/res/menu/edit_label_options_menu.xml @@ -24,7 +24,7 @@ app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34b061a142..844c2fc0f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1030,6 +1030,7 @@ Salvation Underline Import & Export + Export Export as %s Import %s Import from App Database file From 8c7c2cbe94a18621ba7f9e1219775634285243f4 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 16:58:24 +0300 Subject: [PATCH 07/20] Show window button always (as it contains important functions for single windows too) --- .../bible/android/view/activity/bookmark/ManageLabels.kt | 9 --------- .../android/view/activity/page/screen/BibleFrame.kt | 9 ++------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt b/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt index 96a52cbe67..fabc85eb77 100644 --- a/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt +++ b/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt @@ -32,7 +32,6 @@ import android.text.style.ImageSpan import android.util.Log import android.view.Menu import android.view.MenuItem -import android.view.View import android.widget.Button import android.widget.ListView import android.widget.TextView @@ -62,22 +61,15 @@ import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlin.random.Random.Default.nextInt -import android.view.ViewGroup import android.view.inputmethod.InputMethodManager -import android.widget.LinearLayout -import kotlinx.serialization.SerializationException import kotlinx.serialization.Transient import kotlinx.serialization.json.Json import net.bible.android.control.page.window.WindowControl import net.bible.android.database.IdType -import net.bible.android.database.LogEntryTypes -import net.bible.service.common.CommonUtils.convertDipsToPx import net.bible.service.common.CommonUtils.getResourceColor import net.bible.service.common.displayName import net.bible.service.db.BookmarksUpdatedViaSyncEvent -import net.bible.service.db.importDatabaseFile import kotlin.collections.ArrayList -import net.bible.service.device.ScreenSettings import java.util.regex.PatternSyntaxException private const val TAG = "BookmarkLabels" @@ -330,7 +322,6 @@ class ManageLabels : ListActivityBase() { R.id.help -> help() R.id.newLabel -> newLabel() R.id.resetButton -> reset() - R.id.import_studypads -> importDatabaseFile() R.id.reOrder -> updateLabelList(rePopulate = true, reOrder = true) android.R.id.home -> saveAndExit() else -> isHandled = false diff --git a/app/src/main/java/net/bible/android/view/activity/page/screen/BibleFrame.kt b/app/src/main/java/net/bible/android/view/activity/page/screen/BibleFrame.kt index 2bfcef6535..695627959d 100644 --- a/app/src/main/java/net/bible/android/view/activity/page/screen/BibleFrame.kt +++ b/app/src/main/java/net/bible/android/view/activity/page/screen/BibleFrame.kt @@ -145,15 +145,10 @@ class BibleFrame( } private fun addWindowButton() { - val isSingleWindow = windowControl.isSingleWindow if (allViews.hideWindowButtons) return if (windowRepository.isMaximized) return - val button = - when { - isSingleWindow -> return - else -> createWindowMenuButton(window) - } + val button = createWindowMenuButton(window) if (!mainBibleActivity.isSplitVertically) { button.translationY = mainBibleActivity.topOffset2.toFloat() @@ -168,7 +163,7 @@ class BibleFrame( windowButton = button addView(button, LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, - if (isSingleWindow) Gravity.BOTTOM or Gravity.END else Gravity.TOP or Gravity.END)) + Gravity.TOP or Gravity.END)) } From b74c24b34c3ebb31496b1d78c37f2dd7bddc7106 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 17:19:26 +0300 Subject: [PATCH 08/20] Show info box when exporting studypads --- .../android/view/activity/base/Dialogs.kt | 24 +++++++++++++++++-- .../activity/page/screen/SplitBibleArea.kt | 2 +- .../net/bible/service/db/ExportStudyPads.kt | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt index dfdb547788..18a18f9ed1 100644 --- a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt +++ b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt @@ -18,8 +18,6 @@ package net.bible.android.view.activity.base import android.app.AlertDialog import android.content.Context -import android.os.Build -import android.text.Html import android.text.method.LinkMovementMethod import android.util.Log import android.widget.TextView @@ -29,6 +27,7 @@ import kotlinx.coroutines.withContext import net.bible.android.BibleApplication.Companion.application import net.bible.android.activity.R import net.bible.android.control.report.ErrorReportControl +import net.bible.service.common.CommonUtils import net.bible.service.common.htmlToSpan import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -171,4 +170,25 @@ object Dialogs { val messageStr = if(message == null) null else context.getString(message) simpleQuestion(context, message = messageStr, title = titleStr) } + suspend fun simpleInfoMessage(context: Context, key: String, message: String? = context.getString(R.string.are_you_sure)) = withContext(Dispatchers.Main) { + suspendCoroutine { + if (CommonUtils.settings.getBoolean("skip_$key", false)) { + it.resume(true) + return@suspendCoroutine + } + AlertDialog.Builder(context) + .setMessage(message) + .setPositiveButton(R.string.okay) { _, _ -> it.resume(true) } + .setNeutralButton(R.string.dont_show) { _, _ -> + CommonUtils.settings.setBoolean("skip_$key", true) + it.resume(true) + } + .show() + } + } + + suspend fun simpleInfoMessage(context: Context, key: String, message: Int? = R.string.are_you_sure): Boolean { + val messageStr = if(message == null) null else context.getString(message) + return simpleInfoMessage(context, key, messageStr) + } } diff --git a/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt b/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt index 6ef0b2d75d..a6b0aa685d 100644 --- a/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt +++ b/app/src/main/java/net/bible/android/view/activity/page/screen/SplitBibleArea.kt @@ -723,7 +723,7 @@ class SplitBibleArea(private val mainBibleActivity: MainBibleActivity): FrameLay exportHtml.title = app.getString(R.string.export_fileformat, "HTML") val exportStudypad = menu.findItem(R.id.exportStudypad) - exportStudypad.title = app.getString(R.string.export_fileformat, app.getString(R.string.studypad)) + exportStudypad.title = app.getString(R.string.export_something, app.getString(R.string.studypad)) synchronized(BookName::class.java) { val oldValue = BookName.isFullBookName() diff --git a/app/src/main/java/net/bible/service/db/ExportStudyPads.kt b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt index 9930b0679b..6596f3a49e 100644 --- a/app/src/main/java/net/bible/service/db/ExportStudyPads.kt +++ b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt @@ -33,6 +33,7 @@ import net.bible.android.database.migrations.getColumnNames import net.bible.android.database.migrations.getColumnNamesJoined import net.bible.android.database.migrations.joinColumnNames import net.bible.android.view.activity.base.ActivityBase +import net.bible.android.view.activity.base.Dialogs import net.bible.service.common.CommonUtils import net.bible.service.common.CommonUtils.grantUriReadPermissions import java.io.BufferedInputStream @@ -124,6 +125,7 @@ private fun copyStudyPad( } suspend fun exportStudyPads(activity: ActivityBase, vararg labels: BookmarkEntities.Label) = withContext(Dispatchers.IO) { + Dialogs.simpleInfoMessage(activity, "export_studypads_help", R.string.export_studypads_help) val exportDbFile = CommonUtils.tmpFile val exportDb = DatabaseContainer.instance.getBookmarkDb(exportDbFile.absolutePath) exportDb.openHelper.writableDatabase.use {} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 844c2fc0f1..cefe069cbd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1032,6 +1032,7 @@ Import & Export Export Export as %s + Export %s Import %s Import from App Database file Importing from App Database failed. Database file could be wrong format or made with too old version of %s. @@ -1223,4 +1224,5 @@ Database for %s can be either restored (i.e. existing database is removed and selected database is loaded) or imported (selected database is imported into existing database). What do you want to do? Restore Import + File containing exported StudyPads (i.e Labels and Bookmarks) can be imported via Main Menu -> Backup & Restore. From 53caa25125aa597ca5b80c35a3740a47dcd59920 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 18:29:01 +0300 Subject: [PATCH 09/20] Implement multi-select studypads option in StudyPads view and ManageLabels view --- .../android/control/backup/BackupControl.kt | 58 +++---------------- .../android/view/activity/base/Dialogs.kt | 45 ++++++++++++++ .../view/activity/bookmark/ManageLabels.kt | 22 +++++++ .../net/bible/service/db/ExportStudyPads.kt | 20 +++++++ .../res/menu/manage_labels_options_menu.xml | 10 ++++ app/src/main/res/values/strings.xml | 2 +- 6 files changed, 107 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index 198c9c9379..9f628c55bb 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -218,54 +218,6 @@ object BackupControl { return BibleApplication.application.getString(id) } - private suspend fun selectModules(context: Context): List? { - var result: List? = null - withContext(Dispatchers.Main) { - result = suspendCoroutine { - val books = Books.installed().books.filter { !it.isPseudoBook }.sortedBy { it.language } - val bookNames = books.map { - context.getString(R.string.something_with_parenthesis, it.name, "${it.initials}, ${it.language.code}") - }.toTypedArray() - - val checkedItems = bookNames.map { false }.toBooleanArray() - val dialog = AlertDialog.Builder(context) - .setPositiveButton(R.string.okay) { d, _ -> - val selectedBooks = books.filterIndexed { index, book -> checkedItems[index] } - if(selectedBooks.isEmpty()) { - it.resume(null) - } else { - it.resume(selectedBooks) - } - } - .setMultiChoiceItems(bookNames, checkedItems) { _, pos, value -> - checkedItems[pos] = value - } - .setNeutralButton(R.string.select_all) { _, _ -> it.resume(null) } - .setNegativeButton(R.string.cancel) { _, _ -> it.resume(null) } - .setOnCancelListener { _ -> it.resume(null)} - .setTitle(getString(R.string.backup_modules_title)) - .create() - - dialog.setOnShowListener { - dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener { - val allSelected = checkedItems.find { !it } == null - val newValue = !allSelected - val v = dialog.listView - for (i in 0 until v.count) { - v.setItemChecked(i, newValue) - checkedItems[i] = newValue - } - (it as Button).text = getString(if (allSelected) R.string.select_all else R.string.select_none) - } - } - dialog.show() - CommonUtils.fixAlertDialogButtons(dialog) - } - } - Log.i(TAG, "Selected modules to be backed up: ${result?.joinToString(",") { it.initials }}") - return result - } - private suspend fun selectDatabaseSections(context: Context, available: List): List { var result: List withContext(Dispatchers.Main) { @@ -425,7 +377,15 @@ object BackupControl { val fileName = MODULE_BACKUP_NAME internalDbBackupDir.mkdirs() val zipFile = File(internalDbBackupDir, fileName) - val books = selectModules(callingActivity) ?: return@withContext + val books = Dialogs.multiselect( + callingActivity, + R.string.backup_modules_title, + Books.installed().books.filter { !it.isPseudoBook }.sortedBy { it.language } + ) { + callingActivity.getString(R.string.something_with_parenthesis, it.name, "${it.initials}, ${it.language.code}") + } + + if (books.isEmpty()) return@withContext val hourglass = Hourglass(callingActivity) hourglass.show() diff --git a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt index 18a18f9ed1..f21c79a8fd 100644 --- a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt +++ b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt @@ -20,6 +20,7 @@ import android.app.AlertDialog import android.content.Context import android.text.method.LinkMovementMethod import android.util.Log +import android.widget.Button import android.widget.TextView import android.widget.Toast import kotlinx.coroutines.Dispatchers @@ -27,7 +28,9 @@ import kotlinx.coroutines.withContext import net.bible.android.BibleApplication.Companion.application import net.bible.android.activity.R import net.bible.android.control.report.ErrorReportControl +import net.bible.android.database.bookmarks.BookmarkEntities import net.bible.service.common.CommonUtils +import net.bible.service.common.displayName import net.bible.service.common.htmlToSpan import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -191,4 +194,46 @@ object Dialogs { val messageStr = if(message == null) null else context.getString(message) return simpleInfoMessage(context, key, messageStr) } + + suspend fun multiselect(context: Context, title: String, items: List, itemToString: ((arg: T) -> String)? = null): List = suspendCoroutine { + val itemNames = items.map { itemToString?.let { it1 -> it1(it) }?: it.toString() }.toTypedArray() + val checkedItems = itemNames.map { false }.toBooleanArray() + val dialog = AlertDialog.Builder(context) + .setPositiveButton(R.string.okay) { d, _ -> + val selectedItems = items.filterIndexed { index, book -> checkedItems[index] } + if (selectedItems.isEmpty()) { + it.resume(emptyList()) + } else { + it.resume(selectedItems) + } + } + .setMultiChoiceItems(itemNames, checkedItems) { _, pos, value -> + checkedItems[pos] = value + } + .setNeutralButton(R.string.select_all) { _, _ -> it.resume(emptyList()) } + .setNegativeButton(R.string.cancel) { _, _ -> it.resume(emptyList()) } + .setOnCancelListener { _ -> it.resume(emptyList()) } + .setTitle(title) + .create() + + dialog.setOnShowListener { + dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener { + val allSelected = checkedItems.find { !it } == null + val newValue = !allSelected + val v = dialog.listView + for (i in 0 until v.count) { + v.setItemChecked(i, newValue) + checkedItems[i] = newValue + } + (it as Button).text = + context.getString(if (allSelected) R.string.select_all else R.string.select_none) + } + } + dialog.show() + CommonUtils.fixAlertDialogButtons(dialog) + } + + suspend fun multiselect(context: Context, title: Int, items: List, itemToString: ((arg: T) -> String)? = null): List = + multiselect(context, context.getString(title), items, itemToString) + } diff --git a/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt b/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt index fabc85eb77..b68c850aa3 100644 --- a/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt +++ b/app/src/main/java/net/bible/android/view/activity/bookmark/ManageLabels.kt @@ -64,11 +64,13 @@ import kotlin.random.Random.Default.nextInt import android.view.inputmethod.InputMethodManager import kotlinx.serialization.Transient import kotlinx.serialization.json.Json +import net.bible.android.control.backup.BackupControl import net.bible.android.control.page.window.WindowControl import net.bible.android.database.IdType import net.bible.service.common.CommonUtils.getResourceColor import net.bible.service.common.displayName import net.bible.service.db.BookmarksUpdatedViaSyncEvent +import net.bible.service.db.exportStudyPads import kotlin.collections.ArrayList import java.util.regex.PatternSyntaxException @@ -311,6 +313,8 @@ class ManageLabels : ListActivityBase() { override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.manage_labels_options_menu, menu) + menu.findItem(R.id.export_studypads).title = getString(R.string.export_something, getString(R.string.studypads)) + menu.findItem(R.id.import_studypads).title = getString(R.string.import_items, getString(R.string.studypads)) menu.findItem(R.id.resetButton).isVisible = data.hasResetButton menu.findItem(R.id.reOrder).isVisible = data.hasReOrderButton return true @@ -322,6 +326,24 @@ class ManageLabels : ListActivityBase() { R.id.help -> help() R.id.newLabel -> newLabel() R.id.resetButton -> reset() + R.id.export_studypads -> { + lifecycleScope.launch { + val labels = Dialogs.multiselect( + this@ManageLabels, + getString(R.string.export_something, getString(R.string.studypads)), + bookmarkControl.assignableLabels + ) { it.displayName } + if (labels.isNotEmpty()) { + exportStudyPads(this@ManageLabels, *labels.toTypedArray()) + } + } + } + R.id.import_studypads -> { + lifecycleScope.launch { + BackupControl.backupPopup(this@ManageLabels) + updateLabelList(rePopulate = true, reOrder = true) + } + } R.id.reOrder -> updateLabelList(rePopulate = true, reOrder = true) android.R.id.home -> saveAndExit() else -> isHandled = false diff --git a/app/src/main/java/net/bible/service/db/ExportStudyPads.kt b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt index 6596f3a49e..44e42ee97c 100644 --- a/app/src/main/java/net/bible/service/db/ExportStudyPads.kt +++ b/app/src/main/java/net/bible/service/db/ExportStudyPads.kt @@ -36,6 +36,7 @@ import net.bible.android.view.activity.base.ActivityBase import net.bible.android.view.activity.base.Dialogs import net.bible.service.common.CommonUtils import net.bible.service.common.CommonUtils.grantUriReadPermissions +import net.bible.service.common.getFirst import java.io.BufferedInputStream import java.io.File import java.io.FileInputStream @@ -124,6 +125,21 @@ private fun copyStudyPad( """.trimIndent()) } +private fun fixPrimaryLabels(db: SupportSQLiteDatabase) = db.run { + for (table in listOf("BibleBookmark", "GenericBookmark")) { + val fkid = query("SELECT id FROM pragma_foreign_key_list('$table') WHERE `from` = 'primaryLabelId'") + .getFirst { it.getLong(0) } + execSQL(""" + UPDATE $table SET primaryLabelId = NULL + WHERE rowId in ( + SELECT rowid FROM pragma_foreign_key_check('$table') + WHERE parent = "Label" AND fkid = $fkid + ) + """.trimIndent() + ) + } +} + suspend fun exportStudyPads(activity: ActivityBase, vararg labels: BookmarkEntities.Label) = withContext(Dispatchers.IO) { Dialogs.simpleInfoMessage(activity, "export_studypads_help", R.string.export_studypads_help) val exportDbFile = CommonUtils.tmpFile @@ -131,12 +147,16 @@ suspend fun exportStudyPads(activity: ActivityBase, vararg labels: BookmarkEntit exportDb.openHelper.writableDatabase.use {} DatabaseContainer.instance.bookmarkDb.openHelper.writableDatabase.run { execSQL("ATTACH DATABASE '${exportDbFile.absolutePath}' AS export") + execSQL("PRAGMA foreign_keys=OFF;") beginTransaction() for (label in labels) { copyStudyPad(this, label) } + // Primary label(s) of bookmarks might not be included, so let's fix them + fixPrimaryLabels(this) setTransactionSuccessful() endTransaction() + execSQL("PRAGMA foreign_keys=ON;") execSQL("DETACH DATABASE export") } diff --git a/app/src/main/res/menu/manage_labels_options_menu.xml b/app/src/main/res/menu/manage_labels_options_menu.xml index 39505d7616..169bce2680 100644 --- a/app/src/main/res/menu/manage_labels_options_menu.xml +++ b/app/src/main/res/menu/manage_labels_options_menu.xml @@ -38,4 +38,14 @@ android:icon="@drawable/ic_baseline_undo_24" app:showAsAction="never" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cefe069cbd..8ba5f686fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -670,7 +670,7 @@ Your %s database containing bookmarks, notes, reading plans and workspaces is attached Exported StudyPad(s) - Your %s database containing bookmarks, notes related to selected StudyPads is attached + Your %s database containing Bookmarks and Notes related to selected StudyPads is attached Restoring database was unsuccessful. Perhaps you gave invalid database file. Restore Documents from… Send a backup file From 9eef6166ed34913b88e38d184afeaf759a91ca3b Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 18:35:43 +0300 Subject: [PATCH 10/20] Add title to simpleInfoMessage --- .../main/java/net/bible/android/view/activity/base/Dialogs.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt index f21c79a8fd..009fa592eb 100644 --- a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt +++ b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt @@ -180,6 +180,7 @@ object Dialogs { return@suspendCoroutine } AlertDialog.Builder(context) + .setTitle(R.string.information) .setMessage(message) .setPositiveButton(R.string.okay) { _, _ -> it.resume(true) } .setNeutralButton(R.string.dont_show) { _, _ -> From 00d50d39598dd727ec47861455086acb1b8c914c Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 18:51:54 +0300 Subject: [PATCH 11/20] Add overwrite confirmation --- .../android/control/backup/BackupControl.kt | 9 ++++++++ .../android/view/activity/base/Dialogs.kt | 22 ++++++++++--------- app/src/main/res/values/strings.xml | 1 + 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index 9f628c55bb..f907eeda70 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -634,6 +634,15 @@ object BackupControl { val f = File(unzipFolder, "db/${fileName}") if (restore) { + val areYouSure = if (category != null) { + Dialogs.simpleQuestion( + activity, + activity.getString(R.string.overwrite_something, + getString(category.contentDescription) + ) + ) + } else true + if (!areYouSure) continue Log.i(TAG, "Restoring $fileName") DatabaseContainer.instance.dbByFilename[fileName]?.close() val targetFilePath = activity.getDatabasePath(fileName).path diff --git a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt index 009fa592eb..025810684b 100644 --- a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt +++ b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt @@ -158,20 +158,22 @@ object Dialogs { return result } - suspend fun simpleQuestion(context: Context, message: String? = null, title: String? = context.getString(R.string.are_you_sure)) = suspendCoroutine { - AlertDialog.Builder(context) - .setTitle(title) - .setMessage(message) - .setPositiveButton(R.string.okay) { _, _ -> it.resume(true) } - .setNegativeButton(R.string.cancel) { _, _ -> it.resume(false) } - .setOnCancelListener { _ -> it.resume(false) } - .show() + suspend fun simpleQuestion(context: Context, message: String? = null, title: String? = context.getString(R.string.are_you_sure)) = withContext(Dispatchers.Main) { + suspendCoroutine { + AlertDialog.Builder(context) + .setTitle(title) + .setMessage(message) + .setPositiveButton(R.string.okay) { _, _ -> it.resume(true) } + .setNegativeButton(R.string.cancel) { _, _ -> it.resume(false) } + .setOnCancelListener { _ -> it.resume(false) } + .show() + } } - suspend fun simpleQuestion(context: Context, message: Int? = null, title: Int? = R.string.are_you_sure) { + suspend fun simpleQuestion(context: Context, message: Int? = null, title: Int? = R.string.are_you_sure): Boolean { val titleStr = if(title == null) null else context.getString(title) val messageStr = if(message == null) null else context.getString(message) - simpleQuestion(context, message = messageStr, title = titleStr) + return simpleQuestion(context, message = messageStr, title = titleStr) } suspend fun simpleInfoMessage(context: Context, key: String, message: String? = context.getString(R.string.are_you_sure)) = withContext(Dispatchers.Main) { suspendCoroutine { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ba5f686fa..afb07414c9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -656,6 +656,7 @@ Restore app database from… Restore Database File Overwrite My Notes, Bookmarks, Study Pads, Reading Plans and Workspaces? + Overwrite %s? Selected database sections restored successfully My Notes, Bookmarks, Study Pads, Reading Plans and Workspaces restored successfully Downloading backup file… From e1b6be8cb0b75695ee0c27804983c0748e318488 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 18:59:40 +0300 Subject: [PATCH 12/20] Cleanup --- .../main/java/net/bible/android/view/activity/base/Dialogs.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt index 025810684b..3b7c4f4f56 100644 --- a/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt +++ b/app/src/main/java/net/bible/android/view/activity/base/Dialogs.kt @@ -28,9 +28,7 @@ import kotlinx.coroutines.withContext import net.bible.android.BibleApplication.Companion.application import net.bible.android.activity.R import net.bible.android.control.report.ErrorReportControl -import net.bible.android.database.bookmarks.BookmarkEntities import net.bible.service.common.CommonUtils -import net.bible.service.common.displayName import net.bible.service.common.htmlToSpan import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine From 7694777154000d3d03683b981b76d6def829bdda Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 19:05:37 +0300 Subject: [PATCH 13/20] Improve strings --- app/src/main/res/values/strings.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index afb07414c9..d165f54cec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -646,9 +646,11 @@ Bibles, commentaries, dictionaries, maps etc. Backup to… Restore from… + Restore or Import from… Restore Restore or Import + Overwrite %s? What do you want to restore? @@ -656,7 +658,6 @@ Restore app database from… Restore Database File Overwrite My Notes, Bookmarks, Study Pads, Reading Plans and Workspaces? - Overwrite %s? Selected database sections restored successfully My Notes, Bookmarks, Study Pads, Reading Plans and Workspaces restored successfully Downloading backup file… @@ -671,12 +672,17 @@ Your %s database containing bookmarks, notes, reading plans and workspaces is attached Exported StudyPad(s) + Your %s database containing Bookmarks and Notes related to selected StudyPads is attached Restoring database was unsuccessful. Perhaps you gave invalid database file. Restore Documents from… Send a backup file Select which modules to backup Module install cancelled + Database for %s can be either restored (i.e. existing database is removed and selected database is loaded) or imported (selected database is imported into existing database). What do you want to do? + Restore + Import + File containing exported StudyPads (i.e Labels and Bookmarks) can be imported via Main Menu -> Backup & Restore. Copyright: %s @@ -1222,8 +1228,4 @@ Don\'t show this message again - Database for %s can be either restored (i.e. existing database is removed and selected database is loaded) or imported (selected database is imported into existing database). What do you want to do? - Restore - Import - File containing exported StudyPads (i.e Labels and Bookmarks) can be imported via Main Menu -> Backup & Restore. From c82441ff2694cc3067d56ca80968fa7967313e89 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Sat, 25 May 2024 19:30:45 +0300 Subject: [PATCH 14/20] Remove wrong instruction string --- app/src/main/res/values/strings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d165f54cec..fa306dc96f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -672,7 +672,6 @@ Your %s database containing bookmarks, notes, reading plans and workspaces is attached Exported StudyPad(s) - Your %s database containing Bookmarks and Notes related to selected StudyPads is attached Restoring database was unsuccessful. Perhaps you gave invalid database file. Restore Documents from… From 88dc6f074a4d644e293b48d0ccc51af8f10ed7cc Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Mon, 27 May 2024 21:12:32 +0300 Subject: [PATCH 15/20] Let user know the amount of incoming data --- .../android/control/backup/BackupControl.kt | 12 ++++++++---- .../bible/service/cloudsync/SyncUtilities.kt | 1 + .../main/java/net/bible/service/db/ImportDb.kt | 18 ++++++++++++++++++ app/src/main/res/values/strings.xml | 3 +++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index f907eeda70..ca31edf7a5 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -67,6 +67,7 @@ import net.bible.service.cloudsync.CloudSync import net.bible.service.cloudsync.SyncableDatabaseDefinition import net.bible.service.common.CommonUtils.determineFileType import net.bible.service.common.CommonUtils.grantUriReadPermissions +import net.bible.service.db.bookmarksDbStats import net.bible.service.db.importDatabaseFile import net.bible.service.sword.dbFile import net.bible.service.sword.epub.epubDir @@ -626,13 +627,13 @@ object BackupControl { beforeRestore() for (fileName in selection) { val category = SyncableDatabaseDefinition.filenameToCategory[fileName] + val f = File(unzipFolder, "db/${fileName}") val restore = if (category != null) - askIfRestoreOrImport(category, activity) + askIfRestoreOrImport(category, f, activity) else true if (restore == null) continue - val f = File(unzipFolder, "db/${fileName}") if (restore) { val areYouSure = if (category != null) { Dialogs.simpleQuestion( @@ -669,10 +670,13 @@ object BackupControl { true } - private suspend fun askIfRestoreOrImport(category: SyncableDatabaseDefinition, context: ActivityBase): Boolean? = withContext(Dispatchers.Main) { + private suspend fun askIfRestoreOrImport(category: SyncableDatabaseDefinition, backupFile: File, context: ActivityBase): Boolean? = withContext(Dispatchers.Main) { + val contents = if (category == SyncableDatabaseDefinition.BOOKMARKS) { + " (${bookmarksDbStats(category, backupFile)})" + } else "" suspendCoroutine { val message = - context.getString(R.string.ask_restore_or_import, context.getString(category.contentDescription)) + context.getString(R.string.ask_restore_or_import, context.getString(category.contentDescription) + contents) AlertDialog.Builder(context) .setTitle(category.contentDescription) .setMessage(message) diff --git a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt index 660d8254a5..e436ffd40b 100644 --- a/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt +++ b/app/src/main/java/net/bible/service/cloudsync/SyncUtilities.kt @@ -53,6 +53,7 @@ enum class SyncableDatabaseDefinition { READINGPLANS ->ReadingPlanDatabase.dbFileName WORKSPACES -> WorkspaceDatabase.dbFileName } + val tables get() = when(this) { BOOKMARKS -> listOf( Table( diff --git a/app/src/main/java/net/bible/service/db/ImportDb.kt b/app/src/main/java/net/bible/service/db/ImportDb.kt index 7f12d25bda..7023450a01 100644 --- a/app/src/main/java/net/bible/service/db/ImportDb.kt +++ b/app/src/main/java/net/bible/service/db/ImportDb.kt @@ -20,13 +20,31 @@ package net.bible.service.db import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import net.bible.android.activity.R import net.bible.android.database.migrations.getColumnNamesJoined +import net.bible.android.view.activity.page.application import net.bible.service.cloudsync.SyncableDatabaseDefinition +import net.bible.service.common.getFirst import java.io.File import java.lang.Exception private const val TAG = "ImportDb" +suspend fun bookmarksDbStats(category: SyncableDatabaseDefinition, dbFile: File): String = withContext(Dispatchers.IO) { + val dbDef = category.accessor + val importDbFile = dbDef.dbFactory(dbFile.absolutePath) + importDbFile.openHelper.writableDatabase.use { + it.run { + val firstLabel = query("""SELECT name from Label LIMIT 1""").getFirst { it.getString(0) } + val labels = query("""SELECT count(*) from Label""").getFirst { it.getLong(0) } + val bookmarks = + query("""SELECT count(*) from BibleBookmark""").getFirst { it.getLong(0) } + + query("""SELECT count(*) from GenericBookmark""").getFirst { it.getLong(0) } + return@withContext application.getString(R.string.bookmarks_db_stats, firstLabel, (labels - 1).toString(), bookmarks.toString()) + } + } +} + suspend fun importDatabaseFile(category: SyncableDatabaseDefinition, dbFile: File) = withContext(Dispatchers.IO) { val dbDef = category.accessor val importDbFile = dbDef.dbFactory(dbFile.absolutePath) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa306dc96f..427b2aaf03 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -683,6 +683,9 @@ Import File containing exported StudyPads (i.e Labels and Bookmarks) can be imported via Main Menu -> Backup & Restore. + + \"%1$s\" and %2$s other Label(s) / StudyPad/(s) and %3$s Bookmarks + Copyright: %s Installed version: %1$s (%2$s) From a90b0896b5bf68c203e9ae36730797f9dc67c6a6 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Mon, 27 May 2024 21:31:04 +0300 Subject: [PATCH 16/20] Do not show "successfull" message. It's confusing after pressing cancel etc. --- .../main/java/net/bible/android/control/backup/BackupControl.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index ca31edf7a5..2870acf943 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -666,7 +666,6 @@ object BackupControl { hourglass.dismiss() Log.i(TAG, "Restored database successfully") ABEventBus.post(MainBibleActivity.MainBibleAfterRestore()) - Dialogs.showMsg(R.string.restore_success2) true } From 6292b5f525c12aaf73470fb2bfad30275593a69a Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Mon, 27 May 2024 21:31:18 +0300 Subject: [PATCH 17/20] Fix query for bookmarksDbStats --- app/src/main/java/net/bible/service/db/ImportDb.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/bible/service/db/ImportDb.kt b/app/src/main/java/net/bible/service/db/ImportDb.kt index 7023450a01..c52f991fb9 100644 --- a/app/src/main/java/net/bible/service/db/ImportDb.kt +++ b/app/src/main/java/net/bible/service/db/ImportDb.kt @@ -25,6 +25,7 @@ import net.bible.android.database.migrations.getColumnNamesJoined import net.bible.android.view.activity.page.application import net.bible.service.cloudsync.SyncableDatabaseDefinition import net.bible.service.common.getFirst +import net.bible.service.common.getFirstOrNull import java.io.File import java.lang.Exception @@ -35,7 +36,7 @@ suspend fun bookmarksDbStats(category: SyncableDatabaseDefinition, dbFile: File) val importDbFile = dbDef.dbFactory(dbFile.absolutePath) importDbFile.openHelper.writableDatabase.use { it.run { - val firstLabel = query("""SELECT name from Label LIMIT 1""").getFirst { it.getString(0) } + val firstLabel = query("""SELECT name from Label WHERE name NOT LIKE '\_\_%' ESCAPE '\'""").getFirstOrNull { it.getString(0) } ?: '-' val labels = query("""SELECT count(*) from Label""").getFirst { it.getLong(0) } val bookmarks = query("""SELECT count(*) from BibleBookmark""").getFirst { it.getLong(0) } + From 34950de0949303c4bff6bb50b48b73d55e2641b3 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Mon, 27 May 2024 21:37:56 +0300 Subject: [PATCH 18/20] Do not show db selection if there's only 1 db available in backup file --- .../java/net/bible/android/control/backup/BackupControl.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index 2870acf943..a37aea6dbe 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -618,7 +618,11 @@ object BackupControl { Dialogs.showMsg(R.string.restore_unsuccessfull) return@withContext false } - val selection = selectDatabaseSections(activity, containedBackups) + val selection = + if (containedBackups.size > 1) + selectDatabaseSections(activity, containedBackups) + else + containedBackups if (selection.isEmpty()) { return@withContext false From 83c5bd02d36921033aed2339f7d1410231183e4d Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Thu, 6 Jun 2024 11:48:16 +0300 Subject: [PATCH 19/20] Pull translations --- app/src/main/res/values-af/strings.xml | 22 ++++++++++++ app/src/main/res/values-ar/strings.xml | 3 ++ app/src/main/res/values-b+sr+Latn/strings.xml | 3 ++ app/src/main/res/values-b+sr+RS/strings.xml | 3 ++ app/src/main/res/values-cs/strings.xml | 3 ++ app/src/main/res/values-de/strings.xml | 22 ++++++++++++ app/src/main/res/values-el/strings.xml | 35 +++++++++++++------ app/src/main/res/values-en/strings.xml | 2 ++ app/src/main/res/values-eo/strings.xml | 17 ++++++++- app/src/main/res/values-es/strings.xml | 22 ++++++++++++ app/src/main/res/values-et/strings.xml | 3 ++ app/src/main/res/values-fi/strings.xml | 15 ++++++++ app/src/main/res/values-fr/strings.xml | 3 ++ app/src/main/res/values-hi/strings.xml | 3 ++ app/src/main/res/values-hu/strings.xml | 3 ++ app/src/main/res/values-it/strings.xml | 15 ++++++++ app/src/main/res/values-iw/strings.xml | 3 ++ app/src/main/res/values-kk/strings.xml | 3 ++ app/src/main/res/values-ko/strings.xml | 3 ++ app/src/main/res/values-lt/strings.xml | 17 ++++++++- app/src/main/res/values-my/strings.xml | 2 ++ app/src/main/res/values-nb/strings.xml | 3 ++ app/src/main/res/values-nl/strings.xml | 18 ++++++++++ app/src/main/res/values-pl/strings.xml | 17 ++++++++- app/src/main/res/values-pt-rBR/strings.xml | 15 ++++++++ app/src/main/res/values-pt/strings.xml | 15 ++++++++ app/src/main/res/values-ro/strings.xml | 17 ++++++++- app/src/main/res/values-ru/strings.xml | 3 ++ app/src/main/res/values-sk/strings.xml | 21 +++++++++++ app/src/main/res/values-sl/strings.xml | 15 ++++++++ app/src/main/res/values-sv/strings.xml | 3 ++ app/src/main/res/values-ta/strings.xml | 3 ++ app/src/main/res/values-te/strings.xml | 3 ++ app/src/main/res/values-tr/strings.xml | 3 ++ app/src/main/res/values-uk/strings.xml | 3 ++ app/src/main/res/values-yue/strings.xml | 3 ++ app/src/main/res/values-zh-rCN/strings.xml | 16 +++++++++ app/src/main/res/values-zh-rTW/strings.xml | 3 ++ app/src/main/res/values-zh/strings.xml | 3 ++ build.gradle.kts | 2 +- 40 files changed, 353 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 7cbc740af8..1cf7f7e2fc 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -41,6 +41,7 @@ Vind in %s Gaan voort in die agtergrond Herbou + Skep indeks vir %s.... %1s resultate in  %2s @@ -52,6 +53,7 @@ Alle woorde Enige woord Frase + Rou navraag sintaks %d vergelykings gevind Te veel vergelykings. Wys Eerste %d Soek Waar @@ -538,7 +540,11 @@ praat buite die omskrewe versreeks begin het. Bybels, kommentare, woordeboeke, kaarte ens. Rugsteun na ... Herstel vanaf ... + + Herstel of voer in vanaf ... Herstel + Herstel of voer in + Oorskryf %s? Wat wil jy herstel? Rugsteun toep databasis vanaf ... @@ -554,12 +560,20 @@ praat buite die omskrewe versreeks begin het. Aangeheg is jou %1$s modules rugsteun, wat die volgende modules bevat: %2$s %s databasis rugsteun Jou %s databasis met boekmerke, notas, leesplanne en werkruimtes is aangeheg + Uitgevoerde Studieblok(e) + Jou %s databasis wat Boekmerke en Notas bevat, wat verband hou met geselekteerde StudyPads is aangeheg Die herstel van die databasis was onsuksesvol. Miskien het jy \'n ongeldige databasis lêer gegee.   Herstel dokumente vanaf ... Stuur \'n rugsteunlêer Kies watter modules om te rugsteun Module installasie gekanselleer + Databasis vir %s kan óf herstel word (d.w.s. bestaande databasis word verwyder en geselekteerde databasis word gelaai) of ingevoer (geselekteerde databasis word in bestaande databasis ingevoer). Wat wil jy doen? + Herstel + Invoer + Lêer wat uitgevoerde Studiebloke bevat (dws etikette en boekmerke) kan ingevoer word via hoofkieslys -> rugsteun en herstel. + + \"%1$s\" and %2$s ander etiket(te) / studieblad(e) en %3$s Boekmerke Kopiereg:%s Geïnstalleerde weergawe: %1$s (%2$s) @@ -696,6 +710,10 @@ Benewens boekmerke, kan daar persoonlike teksinskrywings in \'n studieblok wees. Soek teks kan spesiale karakters insluit soos *,?, AND, OR, NOT (sien \"Apache Lucene - Query Parser Syntax\" vir meer besonderhede) + Soek teks kan spesiale karakters insluit soos *, AND, OR, NOT + Sien asseblief %s vir meer besonderhede. + Apache Lucene - Navraag Parser Sintaks + FTS5 Vol teks Navraag Sintaks Werkruimtes Met werkruimtes kan daar verskeie konteksafhanklike werksomgewings wees. Werkruimtefunksies kan @@ -748,6 +766,7 @@ n Lys van vorige boeke/dokumente is gevind. Klik hieronder om dit maklik weer af Gebruiker-redigeerbare studie blokkies met persoonlike teks en boekmerke Gebruiker-redigeerbare notas verbonde aan boekmerke My Notas + Studie Blok Studie Blokkies Veelvuldige verwysings Veelvuldige @@ -867,7 +886,9 @@ rapporteer asseblief kwessies aan tekshandhaar Redding Onderlyn Invoer & Uitvoer + Uitvoer Voer uit as %s + Uitvoer %s Invoer %s Voer in uit Program databasis lêer Invoer vanaf Program databasis het misluk. Databasis lêer kan verkeerde formaat wees of gemaak word met te ou weergawe van %s. @@ -890,6 +911,7 @@ rapporteer asseblief kwessies aan tekshandhaar Af Skakels Teks en Skakels + Versteekte skakels Om gedeeltelike of baie verse te kies, kan jy Bybel teks lank druk Toestemming benodig Soek diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index c4466f27bc..93a819d45c 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -526,6 +526,7 @@ الأناجيل، التعليقات، القواميس، الخرائط إلخ. قم بنسخ احتياطي إلى... استعادة من... + إستعادة ماذا تريد أن تستعيد؟ @@ -545,6 +546,8 @@ إرسال ملف النسخة الاحتياطية حدد الوحدات لنسخها احتياطياً. تثبيت الوحدة تم إلغائه. + إستعادة + حقوق النشر: %s النسخة المثبتة: %1$s (%2$s) diff --git a/app/src/main/res/values-b+sr+Latn/strings.xml b/app/src/main/res/values-b+sr+Latn/strings.xml index 2b7d8d5f5b..3b248b91ce 100644 --- a/app/src/main/res/values-b+sr+Latn/strings.xml +++ b/app/src/main/res/values-b+sr+Latn/strings.xml @@ -553,6 +553,7 @@ dovesti do trajnog gubitka ovih obeleživača/beleški ili pak pomeranja njihovi Sveta Pisma, tumačenja, rečnici, karte, itd. Napravi rezervne kopije u... Vratiti iz... + Povratiti Šta želite da povratite? @@ -574,6 +575,8 @@ dovesti do trajnog gubitka ovih obeleživača/beleški ili pak pomeranja njihovi Pošalji datoteku rezervne kopije Odaberi module za rezervnu kopiju Ugradnja modula otkazana + Povratiti + Autorska prava: %s Ugrađena verzija: %1$s (%2$s) diff --git a/app/src/main/res/values-b+sr+RS/strings.xml b/app/src/main/res/values-b+sr+RS/strings.xml index 736f76240b..b51be68bf2 100644 --- a/app/src/main/res/values-b+sr+RS/strings.xml +++ b/app/src/main/res/values-b+sr+RS/strings.xml @@ -554,6 +554,7 @@ Света Писма, тумачења, речници, карте, итд. Направи разервне копије у... Вратити из... + Повратити Шта желите да повратите? @@ -575,6 +576,8 @@ Пошаљи датотеку резервне копије Одабери модуле за резервну копију Уградња модула отказана + Повратити + Ауторска права: %s Уграђена верзија: %1$s (%2$s) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 5e15e69be3..89adf7a8c2 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -550,6 +550,7 @@ bylo spuštěno předčítání mimo stanovený rozsah veršů. Bible, komentáře, slovníky, mapy atd. Zálohovat do... Obnovit z... + Obnovit Které položky chcete obnovit? @@ -571,6 +572,8 @@ bylo spuštěno předčítání mimo stanovený rozsah veršů. Odeslat soubor zálohy Vyberte, které moduly zálohovat Instalace modulu zrušena + Obnovit + Autorská práva: %s nstalovaná verze: %1$s (%2$s) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 33d8ca6bb6..71155b70d4 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -41,6 +41,7 @@ Finde in %s Im Hintergrund fertigstellen Erneut aufbauen + Der Index für %s wird erstellt... %1s Ergebnisse in %2s @@ -52,6 +53,7 @@ allen Wörtern einigen Wörtern genauer Wortfolge + Raw query syntax %d Treffer gefunden Zu viele Treffer. Nur die ersten %d werden angezeigt Suchbereich @@ -521,7 +523,11 @@ Der Wiederholungsmodus wurde automatisch deaktivert, da das Vorlesen außerhalb Bibeln, Kommentare, Wörterbücher, Landkarten etc. Sicherung auf... Wiederherstellen von... + + Wiederherstellen oder importieren von... Wiederherstellen + Wiederherstellung oder Import + Überschreiben von %s? Was möchtest du wiederherstellen? Wiederherstellen der Datenbank von... @@ -537,11 +543,19 @@ Der Wiederholungsmodus wurde automatisch deaktivert, da das Vorlesen außerhalb Angehängt findest du dein %1$s Modulbackup, welches folgende Module enthält: %2$s %s Datenbanksicherung Ihre %s Datenbank mit Lesezeichen, Notizen, Leseplänen und Arbeitsbereichen ist angehängt. + Exportierte Studienmappe(n) + Ihre %s-Datenbank mit Lesezeichen und Notizen zu den ausgewählten StudyPads ist angehängt Die Wiederherstellung der Datenbank ist fehlgeschlagen. Evtl. wurde eine falsche Datei angegeben. Dokumente wiederherstellen von... Sende Backup Zu sichernde Module auswählen Modulinstallation abgebrochen + Die Datenbank für %s kann entweder wiederhergestellt (d. h. die vorhandene Datenbank wird entfernt und die ausgewählte Datenbank wird geladen) oder importiert (die ausgewählte Datenbank wird in die vorhandene Datenbank importiert) werden. Was möchten Sie tun? + Wiederherstellen + Import + Dateien mit exportierten StudyPads (d.h. Etiketten und Lesezeichen) können über Hauptmenü -&gt; Sichern &amp Wiederherstellen importiert werden. + + \"%1$s\" und %2$s andere Label(s) / Studienmappe/(n) und %3$s Lesezeichen Copyright: %s Installierte Version: %1$s (%2$s) @@ -669,6 +683,10 @@ Neben Lesezeichen können auch andere Einträge händisch hinzugefügt werden.Suche Suchtext kann Spezialzeichen wie *,?, AND, OR, NOT enthalten (siehe \"Apache Lucene - Query Parser Syntax für mehr Informationen) + Der Suchtext kann Sonderzeichen wie *, AND, OR, NOT enthalten + Siehe %s für mehr Details. + Apache Lucene - Abfrage-Analyse-Syntax + FTS5 Volltextabfragesyntax Arbeitsbereich Mit Arbeitsbereichen können Sie verschiedene unabhängige Arbeitsumgebungen erstellen. @@ -710,6 +728,7 @@ Eine Liste von vorherigen Büchern/Dokumenten wurde gefunden. Unten klicken, um Bearbeitbare Studienmappen mit benutzerdefiniertem Text und Lesezeichen Benutzereigene editierbare Notizen als Anhang zu Lesezeichen Eigene Notizen + Studienmappe Studienmappen Mehrere Querverweise Multiple @@ -830,7 +849,9 @@ Bitte informiere die Textmaintainer über Probleme Errettung Unterstreichung Import & Export + Export Exportiere als %s + Export %s Importiere %s Importiere von der Datenbankdatei der Applikation Das Importieren von der Datenbankdatei ist fehlgeschlagen. Evtl war die Datei im falschen Format oder wurde mit einer zu alten Version von %s erstellt. @@ -853,6 +874,7 @@ Bitte informiere die Textmaintainer über Probleme Aus Verweise Text und Verweise + Verborgene Verknüpfungen Um Versteile oder mehrere Verse zu markieren, den Bibeltext drücken und halten Genehmigung benötigt Suche diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 417bbbed9d..ef370973b0 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -527,31 +527,43 @@ http://www.heartlight.org, κατόπιν άδειας Βάση δεδομένων Έγγραφα Αρχείο εφαρμογής (.APK) - Σελιδοδείκτες, ετικέτες, σημειώσεις, σημειωματάρια, σχέδια ανάγνωσης, χώροι εργασίας κ.λ.π. + Σελιδοδείκτες, ετικέτες, σημειώσεις, μπλοκ μελέτης, σχέδια ανάγνωσης, χώροι εργασίας κ.λ.π. Άγιες Γραφές, σχoλιασμοί, λεξικά, χάρτες κλπ. Αντίγραφο σε... Επαναφορά από... + + Επαναφορά ή εισαγωγή από... Επαναφορά + Επαναφορά ή εισαγωγή + Αντικατάσταση %s? Τι θέλετε να επαναφέρετε? Επαναφορά βάσης δεδομένων εφαρμογής από... Επαναφορά αρχείου βάσης δεδομένων - Αντικατάσταση Σημειώσεων Μου, σελιδοδεικτών, σημειωματάριων, σχεδίων ανάγνωσης και χώρων εργασίας? + Αντικατάσταση Σημειώσεων Μου, σελιδοδεικτών, Μπλοκ Μελέτης, σχεδίων ανάγνωσης και χώρων εργασίας? Επιλεγμένα τμήματα βάσης δεδομένων επαναφέρθηκαν επιτυχώς - Οι Σημειώσεις Μου, σελιδοδεικτες, σημειωματάρια, σχέδια ανάγνωσης και χώροι εργασίας επαναφέρθηκαν επιτυχώς + Οι Σημειώσεις Μου, σελιδοδεικτες, Μπλοκ Μελέτης, σχέδια ανάγνωσης και χώροι εργασίας επαναφέρθηκαν επιτυχώς Κατέβασμα αντιγράφου ασφαλείας... Επαναφορά βάσης δεδομένων από αντίγραφο... - Οι Σημειώσεις Μου, σελιδοδείκτες, σημειωματάρια, σχέδια ανάγνωσης και χώροι εργασίας αποθηκεύτηκαν επιτυχώς + Οι Σημειώσεις Μου, σελιδοδείκτες, Μπλοκ Μελέτης, σχέδια ανάγνωσης και χώροι εργασίας αποθηκεύτηκαν επιτυχώς Τα επιλεγμένα πρόσθετα ή έγγραφα αποθηκεύτηκαν επιτυχώς. Αντίγραφα προσθέτων %s Τα αντίγραφο σας των προσθέτων %1$s επισυνάπτονται, που περιέχει τα ακόλουθα πρόσθετα: %2$s Αντίγραφο βάσης δεδομένων %s Η βάση δεδομένων %s επισυνάπτεται και περιέχει σελιδοδείκτες, σημειώσεις, σχέδια ανάγνωσης και χώρους εργασίας + Εξαγόμενο(α) μπλοκ μελέτης + Η βάση δεδομένων του %s επισυνάπτεται και περιέχει Σελιδοδείκτες και Σημειώσεις σχετικές με τα επιλεγμένα μπλοκ μελέτης Η επαναφορά της βάσης απέτυχε. Πιθανόν δώσατε άκυρο αρχείο βάσης δεδομένων. Επαναφορά εγγράφων από... Αποστολή αντιγράφου ασφαλείας Επιλέξτε ποιά πρόσθετα θα αντιγράψετε Ακυρώθηκε η εγκατάσταση προσθέτου + Η βάση δεδομένων για %s μπορεί είτε να αντικατασταθεί (π.χ. η υπάρχουσα βάση διαγράφεται και η επιλεγμένη βάση φορτώνεται) είτε να εισαχθεί (η επιλεγμένη βάση εισάγεται μέσα στην υπάρχουσα βάση). Τι θέλετε να κάνετε? + Επαναφορά + Εισαγωγή + Το αρχείο που περιέχει τα εξαγόμενα Μπλοκ μελέτης (π.χ. Ετικέτες και Σελιδοδείκτες) μπορεί να εισαχθεί στο Κύριο Μενού -> Αντίγραφο ασφαλείας και Επαναφορά. + + \"%1$s\" και %2$s άλλη(ες) ετικέτα/(ες) / Μπλοκ Μελέτης και %3$s Σελιδοδείκτες Copyright: %s Εγκατεστημένη έκδοση: %1$s (%2$s) @@ -674,7 +686,7 @@ http://www.heartlight.org, κατόπιν άδειας το αναδυόμενο μενού. Μπορείτε να προσθέσετε τις δικές σας σημειώσεις στον νέο σελιδοδείκτη πατώντας το πλέον επισημασμένο κείμενο στην προβολή Βίβλου. Υπάρχει επίσης ένα ειδικό έγγραφο \"Οι Σημειώσεις μου\" στα Σχόλια που σας επιτρέπει να δείτε τις σημειώσεις σας σε ένα ξεχωριστό παράθυρο. -Τα Σημειωματάρια Μελέτης έχουν σχεδιαστεί να διευκολύνουν την συγγραφή σημειώσεων κηρύγματος,ετοιμασία κηρύγματος ή απλά +Τα Μπλοκ Μελέτης έχουν σχεδιαστεί να διευκολύνουν την συγγραφή σημειώσεων κηρύγματος,ετοιμασία κηρύγματος ή απλά την συγγραφή προσωπικών σημειώσεων Βιβλικής μελέτης που περιέχουν πολλαπλές Βιβλικές παραπομπές. Κάθε Σημειωματάριο Μελέτης συνδέεται με μια ετικέτα σελιδοδείκτη. Όλοι οι Σελιδοδείκτες που έχουν δημιουργηθεί με αυτή την αντίστοιχη ετικέτα εμφανίζονται στο έγγραφο του Σημειωματάριου Μελέτης. Επιπρόσθετα με τους Σελιδοδείκτες, μπορούν να υπάρχουν ιδιόχειρες εισαγωγές κειμένου στο Σημειωματάριο. @@ -732,10 +744,11 @@ http://www.heartlight.org, κατόπιν άδειας Διάλεξε επιλογή strongs Τα ακόλουθα βιβλία δεν ήταν δυνατό να μεταφορτωθούν: Πολλαπλή επιλογή - Σημειωματάρια μελέτης του χρήστη με ιδιόχειρες σημειώσεις και σελιδοδείκτες + Μπλοκ Μελέτης του χρήστη με ιδιόχειρες σημειώσεις και σελιδοδείκτες Σημειώσεις του χρήστη επισυναπτόμενες στους σελιδοδείκτες Οι σημειώσεις μου - Σημειωματάρια Μελέτης + Μπλοκ Μελέτης + Μπλοκ Μελέτης Πολλαπλές παραπομπές Πολλαπλές Σύγκριση μεταφράσεων @@ -825,7 +838,7 @@ http://www.heartlight.org, κατόπιν άδειας Επισήμανση ολόκληρου στίχου Αγαπημένη ετικέτα Επεξεργασία ετικέτας - Απομάκρυνση ετικέτας σελιδοδείκτη \"%s\"? Οι σχετικές σημειώσεις Σημειωματάριου θα αφαιρεθούν επίσης. + Απομάκρυνση ετικέτας σελιδοδείκτη \"%s\"? Τα συνδεδεμένα Μπλοκ Μελέτης θα αφαιρεθούν επίσης. Στυλ υπογράμμισης για σελιδοδείκτες επιλογής Στυλ υπογράμμισης για σελιδοδείκτες ολόκληρου στίχου Επιλεγμένες ετικέτες @@ -854,7 +867,9 @@ http://www.heartlight.org, κατόπιν άδειας Σωτηρία Υπογραμμισμένα Εισαγωγή & εξαγωγή + Εξαγωγή Εξαγωγή ως %s + Εξαγωγή %s Εισαγωγή %s Εισαγωγή από αρχείο βάσης δεδομένων Εισαγωγή από αρχείο βάσης δεδομένων απέτυχε. Λάθος μορφή βάσης ή δομημένη με πολύ παλιά έκδοση του %s @@ -946,13 +961,13 @@ http://www.heartlight.org, κατόπιν άδειας Ποιά τμήματα αντιγράφων θέλετε να επανακτήσετε? Αποθετήρια και σχετικές ρυθμίσεις - Σελιδοδείκτες, Σημειώσεις Μου, Ετικέτες και Σημειωματάρια + Σελιδοδείκτες, Σημειώσεις Μου, Ετικέτες και Μπλοκ Μελέτης Συγχρονισμός... Βρέθηκε στο Cloud, βάση δεδομένων που δεν είναι συγχρονισμένη σε αυτή τη συσκευή. Η βάση περιέχει %s. Τι θέλετε να κάνετε? Σχέδια ανάγνωσης και η κατάσταση τους - Σελιδοδείκτες, Ετικέτες και Σημειωματάρια + Σελιδοδείκτες, Ετικέτες και Μπλοκ Μελέτης Χώροι εργασίας και Παράθυρα Αντιγραφή από το cloud στη συσκευή Αντιγραφή από τη συσκευή στο Cloud diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 1de6378bed..73c489b28c 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -103,7 +103,9 @@ Other labels: all other labels. + + diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index e0b786cbea..95680e2eb0 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -522,7 +522,11 @@ Reĝimo “ripeti alineon” estas malaktiva, ĉar parolo startis ekster de difi Biblioj, komentaroj, vortaroj, mapoj, ktp. Sekurkopii al… Restarigi el… + + Restarigi aŭ enporti el… Restarigi + Restarigi aŭ enporti + Ĉu anstataŭigi %s? Kion vi volas restarigi? Restarigi datumbazon el… @@ -537,12 +541,20 @@ Reĝimo “ripeti alineon” estas malaktiva, ĉar parolo startis ekster de difi %s: sekurkopio de moduloj Sekurkopio de la jenaj moduloj de aplikaĵo %1$s: %2$s troviĝas en la kunsendaĵo. %s: sekurkopio de datumbazo - Datumbazo enhavanta viajn legosignojn, notojn, legplanojn kaj laborspacojn de la programo %s troviĝas en la kunsendaĵo + Datumbazo de la programo %s enhavanta viajn legosignojn, notojn, legplanojn kaj laborspacojn troviĝas en la kunsendaĵo + Elportitaj notblokoj + Datumbazo de la programo %s enhavanta viajn legosignojn kaj notojn por la elektita notbloko troviĝas en la kunsendaĵo Fiaskis restarigi datumbazon. Probable la dosiero estas erara. Restarigi dokumentojn el… Sendi dosieron de sekurkopio Elekti modulojn por sekurkopii Rezignis instali modulon + Datumbazo de %s povas esti restarigita (la ekzista datumbazo foriĝos kaj estos anstataŭigita per la elektata datumbazo) aŭ enportita (la elektata datumbazo estos enportita al la ekzista datumbazo). Kion vi volas fari? + Restarigi + Enporti + Dosieroj enhavantaj elportitajn notblokojn (ekzemple etikedojn kaj legosignojn) povas esti enportitaj per la butono “Sekurkopio” de la ĉefa menuo. + + “%1$s” kaj %2$s alia(j) etikedo(j) / notbloko(j), kaj %3$s legosignoj Aŭtorrajto: %s Instalita versio: %1$s (%2$s) @@ -708,6 +720,7 @@ Trovis liston de antaŭe instalitaj libroj/dokumentoj. Frapetu suben por reelŝu Redakteblaj notblokoj kun propra teksto kaj legosignoj Redakteblaj notoj ligitaj al legosignoj Notoj + notblokon Notblokoj Pluraj referencoj Pluraj @@ -827,7 +840,9 @@ raportu ĝin al prizorganto de modulo Saviĝo Substrekita Enporti aŭ elporti + Elporti Elporti kiel %s + Elporti %s Enporti %s Enporti el dosiero de aplikaĵa datumbazo Fiaskis enporti datumojn el aplikaĵa datumbazo. La dosiero povas esti en malĝusta formo aŭ povas esti farita per tro malnova versio de la aplikaĵo %s. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 72178d8632..2c8c2ce911 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -41,6 +41,7 @@ Encuentra en %s Continuar en el fondo Reconstruir + Crear índice para %s… %1s resultados en %2s @@ -52,6 +53,7 @@ Todas las palabras. Cualquier palabra Frase + Sintaxis de consulta en bruto %d coincidencias encontradas Demasiadas coincidencias. Mostrando las primeras %d Busca Donde @@ -551,7 +553,11 @@ cuando los ajustes se guardan en los correspondientes marcadores. Biblias, comentarios, diccionarios, mapas, etc. Guardar copia de seguridad en... Restaurar copia de seguridad desde... + + Restaurar o importar desde... Resturar + Restaurar o Importar + ¿Sobrescribir %s? ¿Qué quiere restaurar? Restaurar la base de datos de la aplicación desde... @@ -567,11 +573,19 @@ cuando los ajustes se guardan en los correspondientes marcadores. Se adjunta la copia de seguridad de sus módulos de %1$s, que contiene los siguientes módulos: %2$s Copia de seguridad de la base de datos de %s Se adjunta su base de datos de %s que contiene marcadores, mis notas, planes de lectura y espacios + Pizarras de estudio exportados + Se adjunta su base de datos %s que contiene Marcadores y Notas relacionados con los Pizarras de estudio seleccionados La restauración de la base de datos tuvo un error. Tal vez le dio un archivo de base de datos no válido. Restaurar documentos desde... Enviar un archivo de copia de seguridad Seleccione qué módulos desea guardar La instalación del módulo fue cancelada + La base de datos para %s puede restaurarse (es decir, se elimina la base de datos existente y se carga la base de datos seleccionada) o importarse (la base de datos seleccionada se importa en la base de datos existente). ¿Qué desea hacer? + Resturar + Importar + El archivo que contiene las Pizarras de estudio exportadas (es decir, Etiquetas y Marcadores) puede importarse a través de Menú principal -> Copia de seguridad y restauración. + + \"%1$s\" e %2$s otras Etiquetas / Estudios y %3$s Marcadores Copyright: %s Versión instalada: %1$s (%2$s) @@ -710,6 +724,10 @@ Además de los marcadores, puede haber entradas de texto personalizadas en un pi La búsqueda de texto puede incluir caracteres especiales como *, ?, AND, OR, NOT (vea \"Apache Lucene - Query Parser Syntax\" para más detalles) + El texto de búsqueda puede incluir caracteres especiales como *, AND, OR, NOT + Consulte %s para obtener más detalles. + Apache Lucene - Sintaxis del analizador de consultas + FTS5 Sintaxis de consulta de texto completo Espacios Los espacios permiten tener múltiples entornos de trabajo dependientes del contexto. Funciones del espacio @@ -764,6 +782,7 @@ Se encontró una lista de libros / documentos anteriores. Haga clic a continuaci Pizarras de estudio editables por el usuario con texto personalizado y marcadores Notas editables por el usuario adjuntas a los marcadores Mis Notas + Pizarras de estudio Pizarras de Estudio Referencias múltiples Múltiple @@ -894,7 +913,9 @@ informe los problemas a los mantenedores de texto Salvación Subrayar Importar & Exportar + Exportar Exportar cómo %s + Exportar %s Importar %s Importar desde el archivo de la base de datos de la aplicación Error al importar desde la base de datos de la aplicación. El archivo de base de datos puede tener un formato incorrecto o estar hecho con una versión demasiado antigua de %s. @@ -917,6 +938,7 @@ informe los problemas a los mantenedores de texto Apagada Enlaces Texto y Enlaces + Enlaces ocultos Para hace una selección parcial o de muchos versículos puede mantener presionado el texto de la Biblia Se necesita permiso Búsqueda diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 63f7db9925..7bf69d9c69 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -459,6 +459,7 @@ Kas jätkame? Rakenduse fail (.apk) Varunda kohta... Taasta... + Taasta Mida sa soovid taastada? @@ -473,6 +474,8 @@ Kas jätkame? Saada varukoopia fail Vali varundatavad moodulid Mooduli paigaldus katkestatud + Taasta + Autoriõigused: %s Paigaldatud versioon: %1$s (%2$s) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 047821a393..30ea0c2986 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -531,7 +531,11 @@ puhe aloitettiin määrätyn jaejakson ulkopuolelta. Raamatut, kommentaarit, sanakirjat, kartat jne. Varmuuskopioi… Palauta varmuuskopiosta… + + Palauta tai tuo ... Palauta + Palauta tai tuo + Ylikirjoita %s? Mitä haluat palauttaa? Palauta sovelluksen tietokanta... @@ -547,11 +551,19 @@ puhe aloitettiin määrätyn jaejakson ulkopuolelta. Liitteenä on sovelluksen %1$s dokumenttien varmuuskopio, joka sisältää seuraavat moduulit: %2$s Sovelluksen %s tietokannan varmuuskopio Liitetiedostona on sovelluksen %s tietokanta, joka sisältää kirjanmerkit, muistiinpanot, lukusuunnitelmat ja työtilat. + Viedyt opintoalusta(t) + Liitetiedostona on %s tietokanta joka sisältää kirjanmerkit ja muistiinpanot valittuun opintoalustaan liittyen. Tietokannan palauttaminen varmuuskopiosta epäonnistui. Annoit ehkä virheellisen tietokantatiedoston. Palauta dokumentteja… Lähetä varmuuskopiotiedosto Valitse mitkä moduulit varmuuskopioidaan Moduulin asennus peruuntui + %s voidaan joko palauttaa (ts. tietokanta tyhjennetään ja valittu tietokanta ladataan tilalle) tai tuoda (valittu tietokanta tuodaan entiseen tietokantaan). Mitä haluat tehdä? + Palauta + Tuo + Tiedosto jossa on viedyt opintoalustat (ts kirjanmerkkiryhmät ja kirjanmerkit) voidaan tuoda: Päävalikko -> Varmuuskopioi & palauta. + + \"%1$s\" ja %2$s muuta kirjanmerkkiryhmää / opintoalustaa ja %3$s kirjanmerkkiä Kopiointioikeudet: %s Asennettu versio: %1$s (%2$s) @@ -710,6 +722,7 @@ Voit laittaa ikkunoiden kiinnityksen päälle ylävalikosta (valikko joka merkit Opintoalustat joihin käyttäjä voi lisätä tekstiä ja kirjanmerkkejä Kirjanmerkkeihin tehdyt merkinnät Omat merkinnät + Opintoalusta Opintoalustat Useita viittauksia Viitteitä @@ -818,7 +831,9 @@ Voit laittaa ikkunoiden kiinnityksen päälle ylävalikosta (valikko joka merkit Pelastus Alleviivaus Tuo & vie + Vie Vie muodossa %s + Vie %s Tuo muodossa %s Tuo sovelluksen tietokannasta Tuonti sovelluksen tietokannasta epäonnistui. Tietokanta saattaa olla väärässä muodossa tai se on tehty liian vanhalla %s:n versiolla. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e308b4ac2d..a28cb5b789 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -531,6 +531,7 @@ La répétition automatique de passage a été automatiquement désactivée car Bibles, commentaires, dictionnaires, cartes, etc. Sauvegarder vers... Restaurer de... + Restaurer Que voulez-vous restaurer ? @@ -553,6 +554,8 @@ Le fichier est peut-être invalide. Soumettre un fichier de sauvegarde Sélectionner quels modules sauvergarder Installation du module annulée + Restaurer + Copyright: %s Version installée : %1$s (%2$s) diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index b1c21ea58c..aaf132dc5a 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -501,6 +501,7 @@ Wholesome Words के सौजन्य से, http://www.wholesomewords.org/ बैकअप & रिस्टोर पुस्तक बैकअप करे... + बैकअप से नवीकरण आप क्या पुनर्स्थापित करना चाहते हैं? @@ -519,6 +520,8 @@ Wholesome Words के सौजन्य से, http://www.wholesomewords.org/ बैकअप फाइल भेजे बैकअप के लिए मॉड्यूल का चयन करें मॉड्यूल का इंस्टॉल होना रद्द + बैकअप से नवीकरण + कॉपीराइट: %s छंद रचना: %s diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 1a6993059a..9c5b0cb9f6 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -532,6 +532,7 @@ mert a felolvasást a meghatározott szakaszon kívül kezdte. Bibliák, kommentárok, szótárok, térképek, stb. Biztonsági mentés ide... Visszaállítás innen... + Visszaállítás Mi kerüljön visszaállításra? @@ -553,6 +554,8 @@ mert a felolvasást a meghatározott szakaszon kívül kezdte. Biztonsági mentés küldése Válassza ki mely modulokról szeretne biztonsági mentést készíteni Modulok telepítése megszakadt + Visszaállítás + © %s Telepített verzió: %1$s (%2$s) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 3ed767a944..040fa1a84d 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -522,7 +522,11 @@ La ripetizione del passaggio è stata disattivata automaticamente perché il dia Bibbie, commentari, dizionari, mappe ecc. Esegui backup su… Ripristina da… + + Ripristina o Importa da… Ripristina + Ripristina o Importa + Sovrascrivere %s? Cosa vuoi ripristinare? Ripristina il database dell\'app da... @@ -538,11 +542,19 @@ La ripetizione del passaggio è stata disattivata automaticamente perché il dia In allegato il backup dei moduli %1$s, che contiene i seguenti moduli: %2$s Backup del database %s Il tuo database %s contenente segnalibri, annotazioni, piani di lettura e spazi di lavoro è allegato + Appunti di studio esportati + Il tuo database %s contenente Segnalibri e Note relativi agli Appunti di studio selezionati è stato allegato Il ripristino del database non ha avuto esito positivo. Forse hai fornito un file di database non valido. Ripristina documenti da... Invia un file di backup Seleziona i moduli di cui eseguire il backup Installazione del modulo annullata + Il database per %s può essere ripristinato (andando a sostituire quello esistente) oppure importato (il database selezionato verrà importato in quello esistente). Cosa vuoi fare? + Ripristina + Importa + Il file contenente gli Appunti di studio esportati (cioè Etichette e Segnalibri) può essere importato andando in Menu Principale -> Backup e ripristino. + + \"%1$s\" e %2$s altre Etichette / Appunti di studio e %3$s Segnalibri Copyright: %s Versione installata: %1$s (%2$s) @@ -717,6 +729,7 @@ Quando questa funzione è attiva, puoi mostrare diverse finestre fisse nella vis Appunti di studio modificabili con testo e segnalibri personali Annotazioni modificabili allegate ai segnalibri Annotazioni + Appunti di studio Appunti di studio Riferimenti multipli Multipli @@ -847,7 +860,9 @@ Quando questa funzione è attiva, puoi mostrare diverse finestre fisse nella vis Salvezza Sottolinea Importa & esporta + Esporta Esporta come %s + Esporta %s Importa %s Importa dalla base di dati dell\'applicazione L\'importazione del database dell\'app non è riuscita. Il file di database potrebbe essere in un formato sbagliato o creato da una versione troppo vecchia di %s. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index daf0cbc3fd..6ea1104ba0 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -513,6 +513,7 @@ קובץ אפליקציה file (.APK) גיבוי אל... שחזור גיבוי מ... + שחזור מה לשחזר? @@ -532,6 +533,8 @@ שליחת קובץ גיבוי בחירת מודולים לגיבוי התקנת המודול בוטלה + שחזור + כל הזכויות שמורות: %s הגרסה המותקנת: %1$s (%2$s) diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 23ab783662..6b96eaaacf 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -522,6 +522,7 @@ Wholesome Words әдептілігі, http://www.wholesomewords.org/, рұқса Киелі кітаптар, түсіндірмелер, сөздіктер, карталар және т. Сақтық көшірме жасау… Қалпына келтіру… + Қалпына келтіру Сіз нені қалпына келтіргіңіз келеді? @@ -543,6 +544,8 @@ Wholesome Words әдептілігі, http://www.wholesomewords.org/, рұқса Сақтық көшірме файлын жіберу Модульдердің қайсысын сақтық көшіруге жіберетінін таңдау Модульді орнату тоқтатылды + Қалпына келтіру + Авторлық құқық:%s Орнатылған нұсқасы: %1$s (%2$s) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index da103ff0c8..c069f97361 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -479,6 +479,7 @@ 내 참고들 백업과 복원 + 복원 무엇을 복원할까요? @@ -497,6 +498,8 @@ 백업 파일 보내기 어떤 모듈을 백업할지 선택 모듈 설치가 취소됨 + 복원 + 저작권: %s 시형: %s diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 296e122af9..90242c95ad 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -66,7 +66,7 @@ Atverti %s - Ieškoti \"%s\" + Ieškoti „%s“ Skyrių & eilučių numeriai Rodyti skyrių & eilučių numerius @@ -548,7 +548,11 @@ galite šioms knygoms turėti skirtingus garsinio atkūrimo nustatymus. Biblijos, komentarai, žodynai, žemėlapiai ir t.t. Daryti atsarginę kopiją į… Atkurti iš… + + Atkurti arba importuoti iš... Atstatyti + Atkurti arba importuoti + Perrašyti %s? Ką norite atkurti? Atkurti programėlės duomenų bazę iš… @@ -564,11 +568,19 @@ galite šioms knygoms turėti skirtingus garsinio atkūrimo nustatymus. Priede yra jūsų %1$s modulių atsarginė kopija, kurioje yra šie moduliai: %2$s %s duomenų bazės atsarginė kopija Jūsų %s duomenų bazė su žymėmis, pastabomis, skaitymo planais ir darbo sritimis yra pridėta + Eksportuoti bloknotai + Jūsų %s duomenų bazė su Žymėmis ir Pastabomis, susijusiomis su pažymėtais bloknotais, yra prisegta Duomenų bazės atkūrimas buvo nesėkmingas. Galbūt, pateikėte neteisingą duomenų bazės failą. Atkurti dokumentus iš… Siųsti atsarginės kopijos failą Pasirinkite kurių modulių atsargines kopijas darysite Modulio diegimo atsisakyta + %s duomenų bazė gali būti atkurta (t. y. esama duomenų bazė pašalinta, o pasirinkta duomenų bazė įkelta) arba importuota (pasirinkta duomenų bazė importuota į esamą). Ką norite atlikti? + Atkurti + Importuoti + Failas su eksportuotais bloknotais (t.y. etiketėmis ir žymėmis) gali būti importuotas per pagrindinį meniu -> Saugoti ir atkurti atsargines kopijas. + + „%1$s“ ir %2$s kitos etiketės / bloknotai ir %3$s žymės Autorių teisės: %s Įdiegta versija: %1$s (%2$s) @@ -763,6 +775,7 @@ Paspauskite ir palaikykite mygtuką „Atgal“, kad pasiektumėte istorijos są Naudotojo redaguojami bloknotai su įterpiamu tekstu ir žymėmis Naudotojo redaguojamos pastabos, pridėtos prie žymių Mano pastabos + Bloknotas Bloknotai Sudėtinės nuorodos Sudėtinės @@ -893,7 +906,9 @@ Paspauskite ir palaikykite mygtuką „Atgal“, kad pasiektumėte istorijos są Išgelbėjimas Pabraukimas Importuoti ir eksportuoti + Eksportuoti Eksportuoti kaip %s + Eksportuoti %s Importuoti %s Importuoti iš programėlės duomenų bazės failo Nepavyko importuoti iš programėlės duomenų bazės. Gali būti, kad duomenų bazės failas yra neteisingo formato arba sukurtas naudojant per seną %s versiją. diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 62b0ea786a..a6ba3ff566 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -479,6 +479,7 @@ Internal plan နှင့် အမည်တူသည့် sdcard Jsword/readi ကိုယ်ပိုင်မှတ်စုများ & အားအရန်ဖိုင် ပြုလုပ်ပါ; ပြန်ယူပါ + မည်သည့်အရာကို ပြန်ယူလိုပါသနည်း။ အက်ပ် ဒေတာဘေ့စ်အား ...မှ ပြန်ယူပါ @@ -496,6 +497,7 @@ Internal plan နှင့် အမည်တူသည့် sdcard Jsword/readi အရန်ဖိုင်တစ်ခုကို ပို့ပါ အရန်ဖိုင်လုပ်မည့် သင်ခန်းစာများကို ရွေးချယ်ပါ သင်ခန်းစာ ထည့်သွင်းခြင်းအား ပယ်ဖျက်လိုက်ပါပြီ + မူပိုင်ခွင့်: %s ဗားရှင်းဖွဲ့နွဲ့နည်း: %s diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index ddbe4cadc8..bf1066320c 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -414,6 +414,7 @@ Courtesy of Wholesome Words, http://www.wholesomewords.org/, permission granted Sikkerhetskopiering Sikkerhetskopier dokumenter til... + Gjenopprett Skrive over mine notater, bokmerker, studiebrett, leseplaner og arbeidsområder? @@ -428,6 +429,8 @@ Courtesy of Wholesome Words, http://www.wholesomewords.org/, permission granted Send en sikkerhetskopi-fil Velg hvilke moduler som skal sikkerhetskopieres Installasjon av modul avbrutt + Gjenopprett + Opphavsrett: %s Versifisering: %s diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 2354dbd7cd..146e4434d5 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -53,6 +53,7 @@ Alle woorden Elk woord Bewoording + Ruwe query syntax %d items gevonden Te veel items. De eerste %d worden getoond. Waar te zoeken @@ -539,7 +540,11 @@ Herhaal passage modus was automatisch uitgezet doordat spreken was gestart buite Bijbels, commentaren, woordenboeken, kaarten etc. Back-up naar... Herstel van... + + Herstellen of importeren van... Herstellen + Herstellen of Importeren + Overschrijf %s? Wat wilt u herstellen? Terugzetten app database vanuit... @@ -555,11 +560,19 @@ Herhaal passage modus was automatisch uitgezet doordat spreken was gestart buite Toegevoegd is uw %1$s modules back-up, die de volgende modules bevat: %2$s %sdatabase backup Uw %s database met bladwijzers, notities, leesplannen en werkbladen is toegevoegd + Geëxporteerde StudiePad(s) + Jouw %s database bevat Bladwijzers en Notities gerelateerd aan gekoppelde geselecteerde Studiepaden. Database herstellen is mislukt. Misschien gaf u een verkeerd database bestand. Terugzetten Documenten vanuit... Back-up bestand verzenden Selecteer welke modules geback-upt moeten worden Module installatie afgebroken + Database voor %s kan hersteld worden (huidige database wordt verwijderd en geselecteerde database wordt ingeladen) of geïmporteerd (geselecteerde database wordt geïmporteerd in de bestaande database). Wat wil je doen? + Herstellen + Import + Bestand bevat geëxporteerde Studiepaden (Labels en Bladwijzers) die geïmporteerd kunnen worden via Hoofdmenu -> Back-up & Herstellen. + + \"%1$s\" en %2$s andere Label(s) / Studiepad(en) en %3$s Bladwijzers Copyright: %s Geïnstalleerde versie: %1$s (%2$s) @@ -691,6 +704,8 @@ Naast bladwijzers is het ook mogelijk om losse teksten in een studiemap op te ne Zoekterm mag speciale tekens bevatten, zoals *, AND, OR, NOT Zie %s voor meer details. + Apache Lucene - Query Parser Syntax + FTS5 Volledige tekst Query Syntax Werkbladen Werkbladen geven de mogelijkheid om verschillende context-specifieke werk omgevingen te hebben. Werkblad functies kunnen gevonden worden via de Werkbladen knop in het optie-menu (rechtsboven). @@ -734,6 +749,7 @@ Een lijst met eerder gebruikte boeken/documenten is gevonden. Klik hieronder om Door gebruiker bewerkbare studiemappen met aangepaste tekst en bladwijzers Door gebruiker bewerkbare notities gekoppeld aan bladwijzers Mijn Notities + Studiepad Studiemappen Meerdere verwijzingen Meerdere @@ -862,7 +878,9 @@ Een lijst met eerder gebruikte boeken/documenten is gevonden. Klik hieronder om Redding Onderstreping Importeren & Exporteren + Export Exporteer als %s + Export %s Importeer %s Importeren van App Database bestand Importeren vanuit App Database mislukt. Database bestand zou verkeerd formaat kunnen zijn of gemaakt zijn met een te oude versie van %s. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index dcafba6cb1..81b4143e00 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -516,7 +516,11 @@ Tryb „powtórz rozdział” został automatycznie wyłączony, ponieważ rozpo Biblie, komentarze, słowniki, mapy, itd. Utwórz kopię zapasową… Przywróć z kopii zapasowej… + + Przywróć lub importuj z… Przywróć + Przywróć lub importuj + Czy nadpisać %s? Co przywrócić? Przywróć bazę danych z… @@ -531,12 +535,20 @@ Tryb „powtórz rozdział” został automatycznie wyłączony, ponieważ rozpo %s: kopia zapasowa modułów W załączniku znajduje się kopia zapasowa następujących modułów aplikacji %1$s: %2$s. %s: kopia zapasowa bazy danych - W załączniku znajduje się kopia zapasowa bazy danych aplikacji %s zawierająca Twoje zakładki, notatki, plany czytania i obszary robocze + W załączniku znajduje się baza danych aplikacji %s zawierająca Twoje zakładki, notatki, plany czytania i obszary robocze + Wyeksportowane bloczki notatek + W załączniku znajduje się baza danych aplikacji %s zawierająca zakładki i notatki wybranego bloczku notatek Nie udało się przywrócić bazy danych z kopii zapasowej. Prawdopodobnie plik jest uszkodzony. Przywróć dokumenty z… Wyślij plik kopii zapasowej Wybór modułów do kopii zapasowej Przerwano instalację modułu + Baza danych zawierająca %s może zostać przywrócona (istniejąca baza danych zostanie usunięta i zastąpiona wybieraną) lub zaimportowana (wybierana baza danych zostanie połączona z istniejącą). Co chcesz zrobić? + Przywróć + Importuj + Pliki zawierające wyeksportowane bloczki notatek (np. etykiety i zakładki) można zaimportować wybierając „Kopia zapasowa” z menu głównego aplikacji. + + „%1$s” i %2$s innych etykiet / bloczków notatek i %3$s zakładek Prawa autorskie: %s Zainstalowana wersja: %1$s (%2$s) @@ -700,6 +712,7 @@ Znaleziono listę poprzednio zainstalowanych książek/dokumentów. Stuknij, aby Edytowalne bloczki notatek z własnym tekstem i zakładkami Edytowalne notatki dołączane do zakładek Moje notatki + bloczek notatek Bloczki notatek Mnogie odsyłacze Odsyłacze @@ -818,7 +831,9 @@ zgłoś go do opiekuna tego modułu Zbawienie Podkreślony Importuj / eksportuj + Eksportuj Eksportuj jako %s + Eksportuj %s Importuj %s Importuj z pliku bazy danych aplikacji Nie udało się zaimportować z bazy danych aplikacji. Plik bazy danych może być w złym formacie bądź być utworzony przez bardzo starą wersję aplikacji %s. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index ae943318ed..2ca4448e90 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -542,7 +542,11 @@ a fala foi iniciada fora da gama dos versículos. Bíblias, comentários, dicionários, mapas, etc. Cópia de segurança para Restauração de cópia de segurança de ... + + Restaurar ou importar de... Restaurar + Restaurar ou Importar + Sobrescreverr %s? O que deseja restaurar? Restaurar banco de dados do aplicativo a partir de ... @@ -558,11 +562,19 @@ a fala foi iniciada fora da gama dos versículos. Em anexo estão %1$sdos módulos em cópias de segurança, que contêm os seguintes módulos: %2$s %sde cópia de segurança do banco de dados A sua %sbase de dados contém marcadores, notas, planos de leitura e áreas de trabalho em anexo + Áreas de Estudo exportada(s) + O seu banco de dados %s contém Marcadores e Notas relacionados com as Áreas de Estudo selecionadas em anexo. Não foi possível restaurar o banco de dados. Talvez tenha fornecido um arquivo de banco de dados inválido. Restaurar documentos a partir de... Enviar arquivo de cópia de segurança Escolher quais os módulos para incluir na cópia de segurança Instalação de módulos cancelada + O banco de dados para %s pode ser restaurado (ou seja, o banco de dados existente é removido e o banco de dados selecionado é carregado), ou importado (o banco de dados selecionado é importado para o banco de dados existente). O que você pretende fazer? + Restaurar + Importar + O arquivo que contém Blocos de estudo exportados (ou seja, etiquetas e marcadores) pode ser importado através do Menu Principal -> Backup e Restauração. + + \"%1$s\" e %2$s outras Etiqueta(s) / Área de Estudo(s) e %3$s Marcadores Direitos autorais:%s Versão instalada: %1$s(%2$s) @@ -748,6 +760,7 @@ Uma lista de livros / documentos anteriores foi encontrada. Clique abaixo para b Estudos editáveis ​​pelo utilizador, com texto e marcadores personalizados Notas editáveis ​​pelo utilizador, anexadas aos marcadores Minhas notas + Blocos de estudo Blocos de estudo Referências múltiplas Múltiplas @@ -867,7 +880,9 @@ por favor comunique qualquer problema à equipe que mantém os textos Salvação Sublinhado Importar e Exportar + Exportar Exportar como %s + Exportar %s Importar %s Importar do banco de dados A importação a partir do banco de dados do aplicativo falhou. O arquivo do banco de dados pode estar no formato errado ou ter uma versão muito antiga de %s. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 186931cf72..60c399f272 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -529,7 +529,11 @@ a fala foi iniciada fora da gama dos versículos. Bíblias, comentários, dicionários, mapas, etc. Fazer uma cópia de segurança para... Restaurar uma cópia de segurança a partir de... + + Restaurar ou importar de... Restaurar + Restaurar ou Importar + Substituir %s? O que deseja restaurar? Restaurar base de dados da aplicação a partir de... @@ -545,11 +549,19 @@ a fala foi iniciada fora da gama dos versículos. Em anexo estão %1$s dos módulos em backup, que contêm os seguintes módulos:%2$s %sde backup da base de dados A sua %sbase de dados contém marcadores, anotações, planos de leitura e áreas de trabalho em anexo + Áreas de Estudo exportados + A sua base de dados %s contém marcadores e anotações relacionados com as Áreas de Estudo seleccionadas em anexo. Não foi possível restaurar a base de dados. Talvez tenha fornecido um ficheiro de base de dados inválido. Restaurar documentos a partir de... Enviar ficheiro de backup Escolher quais os módulos para incluir no backup Instalação de módulos cancelada + A base de dados para %s pode ser restaurada (ou seja, a base de dados existente é removida e a base de dados selecionada é carregada), ou importada (a base de dados selecionada é importada para a base de dados existente). O que é que pretende fazer? + Restaurar + Importar + O ficheiro que contém Áreas de Estudo exportados (ou seja, etiquetas e marcadores) pode ser importado através do Menu Principal -> Backup e Restauro. + + \"%1$s\" e %2$s outras Etiquetas / Áreas de Estudo e %3$s Marcadores Direitos de Autor:%s Versão instalada: %1$s(%2$s) @@ -735,6 +747,7 @@ Uma lista de livros / documentos anteriores foi encontrada. Clique abaixo para b Áreas de estudo editáveis ​​pelo utilizador, com texto e marcadores personalizados Notas editáveis ​​pelo utilizador, anexadas aos marcadores Anotações + Áreas de Estudo Áreas de Estudo Referências múltiplas Múltiplas @@ -855,7 +868,9 @@ por favor comunique qualquer problema à equipa que mantém os textos Guardar Sublinhar Importar e Exportar + Exportar Exportar como %s + Exportar %s Importar %s Importar a partir de ficheiro da Base de Dados da Aplicação A importação a partir da Base de Dados do aplicativo falhou. O ficheiro da Base de Dados pode estar no formato errado ou ter uma versão muito antiga de %s. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index ae5779bd10..a422b8d001 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -558,7 +558,11 @@ Biblii, comentarii, dicționare, hărți, etc. Copiază în… Recuperează din… + + Recuperează sau importă din… Recuperează + Recuperează sau importă + Înlocuiești %s? Ce vrei să recuperezi? Recuperează baza de date a aplicației din… @@ -574,11 +578,19 @@ Atașat este copia de rezervă a modulelor %1$s care conține următoarele module: %2$s %s copie a bazei de date Baza de date a %s care conține semne de carte, notițe, planuri de citire și spații de studiu este atașată + Notițe de studiu exportate + Baza de date a %s care conține semne de carte și notițele de studiu alese este atașată Restabilirea bazei de date nu a reușit. Poate ai furnizat un fișier greșit. Recuperează documentele din… Trimite o copie de rezervă Alege ce vrei să copiezi Instalarea a fost anulată + Baza de date pentru %s poate să fie recuperată (adică baza de date existentă este eliminată și baza de date aleasă este încărcată) sau importată (baza de date aleasă este importată în baza de date existentă). Ce dorești să faci? + Recuperează + Importă + Fișierul care conține notițele de studiu exportate (adică etichete și semne de carte) poate fi importat prin intermediul opțiunilor principale -> Copie de rezervă & Recuperează. + + „%1$s” și %2$s altă(e) Etichetă(e) / Notiță(e) de studiu și %3$s Semne de carte Drepturi de autor: %s Versiunea instalată: %1$s (%2$s) @@ -779,6 +791,7 @@ Notițe de studiu modificabile cu text și semne de carte Notițe modificabile atașate semnelor de carte Notițele mele + Notiță de studiu Notițe de studiu Trimiteri multiple Multiple @@ -909,7 +922,9 @@ Mîntuire Subliniat Importă și Exportă - Exportă în %s + Exportă + Exportă ca %s + Exportă %s Importă %s Importă din baza de date a aplicației Importarea din baza de date a aplicației a eșuat. Fișierul bazei de date ar putea avea un format greșit sau a fost creat cu o versiune prea veche de %s. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 277b801659..324127fe71 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -552,6 +552,7 @@ http://www.heartlight.org, разрешение предоставлено Библии, комментарии, словари, карты и т. д. Резервное копирование в… Восстановить из… + Восстановить Что вы хотите восстановить? @@ -573,6 +574,8 @@ http://www.heartlight.org, разрешение предоставлено Отправить файл резервной копии Выберите модули для резервного копирования Установка модуля отменена + Восстановить + Авторское право: %s Установленная версия: %1$s(%2$s) diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index b64fc7b911..f6cf8e7fe6 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -41,6 +41,7 @@ Hľadať v %s Pokračovať na pozadí Znova vytvoriť + Vytváranie indexu pre %s… %1s výsledkov v %2s @@ -52,6 +53,7 @@ Všetky slová Ktorékoľvek slovo Fráza + Syntax surového dopytu Nájdené výsledky: %d Príliš veľa výsledkov. Zobrazujem prvých %d Kde hľadať @@ -552,7 +554,11 @@ Režim opakovania bol vypnutý, pretože Biblie, komentáre, slovníky, mapy atď. Zálohovať do… Obnoviť z… + + Obnoviť alebo importovať z… Obnoviť + Obnoviť alebo importovať + Prepísať%s? Čo chcete obnoviť? Obnoviť databázu aplikácie z… @@ -568,11 +574,18 @@ Režim opakovania bol vypnutý, pretože V prílohe je %1$s záloha vašich modulov, ktorá obsahuje nasledujúce moduly: %2$s záloha databázy %s Vaša databáza %s obsahujúca záložky, poznámky, plán čítania a pracovnej plochy je pripojená. + Vyexportované Študijné zložky + Vaša databáza %s, obsahujúca záložky a poznámky týkajúce sa vybraných Študijných zložiek, je pripojená Obnovenie databázy nebolo úspešné. Pravdepodobne ste zadali neplatný databázový súbor. Obnoviť Dokumenty z… Odoslať zálohovací súbor Vybrať, ktoré moduly zálohovať Inštalovanie modulov prerušené + Databáza pre %s môže byť buď obnovená (t. j. existujúca databáza sa odstráni a vybraná databáza sa načíta) alebo importovaná (vybraná databáza sa importuje do existujúcej databázy). Čo si prajete vykonať? + Obnoviť + Import + Súbor obsahujúci vyexportované Študijné zložky (t. j. Štítky a Záložky) je možné importovať z Hlavnej ponuky -> Záloha & Obnoviť. + Copyright: %s Inštalovaná verzia: %1$s (%2$s) @@ -713,6 +726,10 @@ Režim opakovania bol vypnutý, pretože Vyhľadávaný text môže obsahovať aj špeciálne znaky ako *,?, AND, OR, NOT (viď \"Apache Lucene - Query Parser Syntax\" pre viac detailov) + Hľadaný text môže obsahovať špeciálne znaky ako *, AND, OR, NOT + Ďalšie podrobnosti nájdete v%s. + Syntax analyzátora dopytov – Apache Lucene + Syntax celotextového dopytu FTS5 Pracovné plochy Pracovné plochy umožňujú mať viacero pracovných prostredí závislých od kontextu. Funkcie pracovnej plochy @@ -768,6 +785,7 @@ Režim opakovania bol vypnutý, pretože Používateľom upravovateľné študijné zložky s vlastným textom a záložkami Používateľom upravovateľné študijné zložky pripojené k záložkám Moje poznámky + Študijná zložka Študijné zložky Viac referencií Viac @@ -897,7 +915,9 @@ Režim opakovania bol vypnutý, pretože Spasenie Podčiarknutie Import a Export + Export Exportovať ako %s + Exportovať %s Importovať %s Importovať z databázového súboru aplikácie Import z databázy aplikácie zlyhal. Databázový súbor môže mať nesprávny formát alebo môže byť vytvorený v príliš starej verzií %s. @@ -920,6 +940,7 @@ Režim opakovania bol vypnutý, pretože Vypnuté Odkazy Texty a odkazy + Skryté odkazy Dlhým stlačením Biblického textu označíte časť alebo viacero veršov Vyžaduje sa povolenie Hľadať diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 4152b3f883..6b964041d6 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -554,7 +554,11 @@ poglavij, toda Rimljane počasneje, z zvoki ob spremembah poglavja, potem Biblije, komentarji, slovarji, zemljevidi, itd. Varnostno kopiranje v… Obnovi iz… + + Obnovi ali uvozi iz ... Povrni iz arhiva + Obnovi ali uvozi iz + Prepišem %s? Kaj želite obnoviti? Obnovitev baze aplikacije iz… @@ -570,11 +574,19 @@ poglavij, toda Rimljane počasneje, z zvoki ob spremembah poglavja, potem Priložena je varnostna kopija %1$s modulov, ki vsebuje naslednje module: %2$s %s varnostno kopiranje Vaša %s baza podatkov, ki vsebuje zaznamke, opombe, načrte za branje in delovne prostore, je pripeta + Izvoženi študijski zaznamki + Vaša %s baza podatkov, ki vsebuje zaznamke in opombe, povezane z izbranimi študijskimi zaznamki, je priložena Obnovitev baze podatkov ni bila uspešna. Morda ste navedli neveljavno datoteko baze podatkov. Obnovi dokumente iz… Pošlji arhivsko datoteko Izberite, katere module želite varnostno kopirati Namestitev modula je preklicana + Bazo podatkov za %s je mogoče obnoviti (tj. obstoječo zbirko podatkov odstranimo in naložimo izbrano zbirko podatkov) ali uvozimo (izbrano bazo podatkov uvozimo v obstoječo zbirko podatkov). Kaj želite storiti? + Povrni iz arhiva + Uvoz + Datoteko, ki vsebuje izvožene študijske zaznamke (tj. oznake in zaznamke), lahko uvozite prek glavnega menija -> Varnostno kopiranje & Obnovi. + + \"%1$s\" in %2$s druge Oznake / Študijske opombe in %3$s Zaznamki Avtorske pravice: %s Nameščena verzija: %1$s (%2$s) @@ -762,6 +774,7 @@ Iskanje lahko vsebuje posebne znake kot *, ?, AND, OR, NOT Uporabniške beležnice za urejanje z besedilom in zaznamki po meri Uporabniško urejene opombe, priložene zaznamkom Moje opombe + Študijski zaznamki Beležke Več referenc Več @@ -891,7 +904,9 @@ Iskanje lahko vsebuje posebne znake kot *, ?, AND, OR, NOT Rešitev duše Podčrtaj Uvoz in izvoz + Izvoz Izvozi kot %s + Izvoz %s Uvozi %s Uvoz iz podatkovne datoteke Uvoz iz podatkovne datoteke ni uspel. Datoteka je lahko napačne oblike ali pa je narejena s prestaro različico%s. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 5e888e1a84..938822db3a 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -404,6 +404,7 @@ i GT och NT varje dag. Anteckningar + Återställ Vill du återställa? @@ -413,6 +414,8 @@ i GT och NT varje dag. Skicka en säkerhetskopieringsfil Välj vilken modul att säkerhetskopiera Modulinstallation avbruten + Återställ + Copyright: %s diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 4015cc2c2f..ade1c0f012 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -553,6 +553,7 @@ விவிலியம், விளக்க உரைகள், அகராதிகள், வரைபடங்கள் மற்றும் பல. ...க்கு நகல் எடு .... இல் இருந்து மீட்டமை + மீட்டமை எதை மீட்டமைக்க வேண்டும்? @@ -574,6 +575,8 @@ நகல் கோப்பை அனுப்பு நகல் எடுக்கவேண்டிய மாடியுல்களை தெரிவு செய்க மாடியுல்களை நிறுவுவது ரத்து செய்யப்பட்டது + மீட்டமை + காப்புரிமை: %s நிறுவப்பட்ட பதிப்பு: %1$s (%2$s) diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index f35a3db5b6..9b168312a8 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -544,6 +544,7 @@ http://www.heartlight.org, అనుమతి మంజూరు చేయబడ బుక్‌మార్క్‌లు, లేబుల్‌లు, నోట్స్, స్టడీ ప్యాడ్‌లు, రీడింగ్ ప్లాన్‌లు, వర్క్‌స్పేస్‌లు మొదలైనవి. ఇక్కడికి బ్యాకప్… దీని నుండి పునరుద్ధరించు… + పునరుద్ధరించు మీరు ఏమి పునరుద్ధరించాలనుకుంటున్నారు? @@ -565,6 +566,8 @@ http://www.heartlight.org, అనుమతి మంజూరు చేయబడ బ్యాకప్ ఫైల్‌ను పంపండి ఏ మాడ్యూల్‌లను బ్యాకప్ చేయాలో ఎంచుకోండి మాడ్యూల్ ఇన్‌స్టాల్ రద్దు చేయబడింది + పునరుద్ధరించు + కాపీరైట్: %s ఇన్‌స్టాల్ చేసిన వెర్షన్: %1$s (%2$s) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 1cebdb637a..80f49eebf5 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -556,6 +556,7 @@ Kutsal Kitaplar, yorum kitaplar, sözlükler, haritalar vb. Yedekleme hedefi… Geri yükle kaynağı… + Yedekten geri yükle Neyi geri yüklemek istiyorsunuz? @@ -577,6 +578,8 @@ Yedekleme dosyasını gönder Hangi modüllerin yedekleneceğini seçin Modül yüklemesi iptal edildi + Yedekten geri yükle + Telif hakkı: %s Yüklü sürüm: %1$s (%2$s) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index ad110453bd..75397aac12 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -547,6 +547,7 @@ Біблії, коментарі, словники, карти тощо Резервне копіювання в ... Відновити з... + Відновлення Що ви хочете відновити? @@ -568,6 +569,8 @@ Надіслати файл резервної копії Виберіть модулі для резервного копіювання Встановлення модуля скасовано + Відновлення + Aвторські права: %s Встановлена версія: %1$s (%2$s) diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index 48a4597be1..354b96119e 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -547,6 +547,7 @@ http://www.heartlight.org 准予使用 聖經、註釋、字典、地圖等。 備份至... 回復至... + 回復 你想回復什麼? @@ -568,6 +569,8 @@ http://www.heartlight.org 准予使用 傳送備份檔案 選擇備份模組 模組安裝已取消 + 回復 + 版權: %s 已安裝版本:%1$s (%2$s) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c5f962dc0b..a2c2dd5562 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -41,6 +41,7 @@ 在 %s 中查找 在后台运行 重建 + 为%s建立索引 在 %2s 中找到 %1s 个结果 @@ -52,6 +53,7 @@ 所有的词 任何一词 完整句子 + 原始查询句法 匹配到 %d 条结果 符合条件的结果过多。仅显示前面的 %d 条 搜寻范围 @@ -550,7 +552,11 @@ http://www.heartlight.org 准予使用 圣经、评论、词典、地图等。 备份到... 从备份恢复... + + 从指定地方恢复或导入 恢复 + 恢复或导入 + 是否覆盖%s? 你想恢复什么? 从指定地方恢复程序资料库 @@ -566,11 +572,16 @@ http://www.heartlight.org 准予使用 此附你的 %1$s 模组备份,包含了以下模组: %2$s %s 资料库备份 你的%s载有书签、笔记、读经计划及工作检视的资料库已包括在内 + 导出学习笔记 + 学习计划相关的%s数据库已为您添加,包含了书签、笔记 未能回复资料库。或许你的资料库档案无效。 从指定地方恢复文档 传送备份档案 选择备份模组 模组安装已取消 + 恢复 + 导入 + 版权: %s 安装的版本:%1$s(%2$s) @@ -700,6 +711,8 @@ Beta用家请透过 %s 、或Google Play回报错误。 搜寻字词可以加入特殊字符如 * , ?, AND, OR, NOT (有关详细信息请参「Apache Lucene - Query Parser Syntax」) + 搜索文本可包括特殊符号,比如*, AND, OR, NOT + 更多详情请见%s 工作检视 工作检视可让你打开数个工作环境。 @@ -747,6 +760,7 @@ Beta用家请透过 %s 、或Google Play回报错误。 使用者可自行编辑个人笔记及标记书签 书签已附带使用者的笔记 笔记列表 + 学习笔记 个人笔记 多重参考 多重 @@ -877,7 +891,9 @@ Beta用家请透过 %s 、或Google Play回报错误。 救恩 下划线 导入与导出 + 导出 以%s导出 + 导出%s 以%s导入 从应用程序数据库文件导入 从应用程序数据库导入失败。数据库文件版本可能太旧,或者是从太旧的%s建立。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 48a4597be1..354b96119e 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -547,6 +547,7 @@ http://www.heartlight.org 准予使用 聖經、註釋、字典、地圖等。 備份至... 回復至... + 回復 你想回復什麼? @@ -568,6 +569,8 @@ http://www.heartlight.org 准予使用 傳送備份檔案 選擇備份模組 模組安裝已取消 + 回復 + 版權: %s 已安裝版本:%1$s (%2$s) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 48a4597be1..354b96119e 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -547,6 +547,7 @@ http://www.heartlight.org 准予使用 聖經、註釋、字典、地圖等。 備份至... 回復至... + 回復 你想回復什麼? @@ -568,6 +569,8 @@ http://www.heartlight.org 准予使用 傳送備份檔案 選擇備份模組 模組安裝已取消 + 回復 + 版權: %s 已安裝版本:%1$s (%2$s) diff --git a/build.gradle.kts b/build.gradle.kts index b23b28a8a2..72dc865f04 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ buildscript { val jvmToolChainVersion by extra(17) val coreKtxVersion by extra("1.13.1") val sqliteAndroidVersion by extra("3.42.0") - val jswordVersion by extra("2.4.13") + val jswordVersion by extra("2.4.14") repositories { From dfcfbe53ff3765890f5d4ac0c2d360b1747acbb6 Mon Sep 17 00:00:00 2001 From: Tuomas Airaksinen Date: Thu, 6 Jun 2024 13:14:03 +0300 Subject: [PATCH 20/20] Do not disable sync if only only importing stuff --- .../android/control/backup/BackupControl.kt | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt index a37aea6dbe..f74f1c28cb 100644 --- a/app/src/main/java/net/bible/android/control/backup/BackupControl.kt +++ b/app/src/main/java/net/bible/android/control/backup/BackupControl.kt @@ -191,7 +191,9 @@ object BackupControl { } if(version <= OLD_DATABASE_VERSION) { Log.i(TAG, "Loading from backup database with version $version") - beforeRestore() + beforeRestore(SyncableDatabaseDefinition.BOOKMARKS) + beforeRestore(SyncableDatabaseDefinition.WORKSPACES) + beforeRestore(SyncableDatabaseDefinition.READINGPLANS) DatabaseContainer.reset() // When restoring old style db, we need to remove all databases first deleteAllDatabases() @@ -559,26 +561,22 @@ object BackupControl { return version <= maxDatabaseVersion(file.name) } - private suspend fun beforeRestore() { + private suspend fun beforeRestore(category: SyncableDatabaseDefinition) { if(DatabaseContainer.ready && CloudSync.signedIn) { - for (it in DatabaseContainer.databaseAccessors) { - it.category.syncEnabled = false - } + category.syncEnabled = false ABEventBus.post(ToastEvent(R.string.disabling_sync)) CloudSync.waitUntilFinished() - CloudSync.signOut() } } - private suspend fun afterRestore(selection: List? = null) { - for (it in DatabaseContainer.databaseAccessors) { - it.category.syncEnabled = false - } - for(s in selection?: ALL_DB_FILENAMES.toList()) { + private suspend fun afterRestore(restoredSelection: List? = null) { + val selection: List = restoredSelection?: SyncableDatabaseDefinition.ALL.toList() + for(s in selection) { + s.syncEnabled = false val db: SyncableRoomDatabase? = when(s) { - BookmarkDatabase.dbFileName -> DatabaseContainer.instance.bookmarkDb - ReadingPlanDatabase.dbFileName -> DatabaseContainer.instance.readingPlanDb - WorkspaceDatabase.dbFileName -> DatabaseContainer.instance.workspaceDb + SyncableDatabaseDefinition.BOOKMARKS -> DatabaseContainer.instance.bookmarkDb + SyncableDatabaseDefinition.READINGPLANS -> DatabaseContainer.instance.readingPlanDb + SyncableDatabaseDefinition.WORKSPACES -> DatabaseContainer.instance.workspaceDb else -> null } if(db != null) { @@ -604,7 +602,7 @@ object BackupControl { tmpFile.outputStream().use {inputStream.copyTo(it) } CommonUtils.unzipFile(tmpFile, unzipFolder) - val selection = + val restoredSelection = Closeable { tmpFile.delete() unzipFolder.deleteRecursively() @@ -623,12 +621,11 @@ object BackupControl { selectDatabaseSections(activity, containedBackups) else containedBackups - + val restoredSelection = ArrayList() if (selection.isEmpty()) { return@withContext false } hourglass.show() - beforeRestore() for (fileName in selection) { val category = SyncableDatabaseDefinition.filenameToCategory[fileName] val f = File(unzipFolder, "db/${fileName}") @@ -639,6 +636,11 @@ object BackupControl { if (restore == null) continue if (restore) { + if(category != null) { + restoredSelection.add(category) + beforeRestore(category) + } + val areYouSure = if (category != null) { Dialogs.simpleQuestion( activity, @@ -661,11 +663,11 @@ object BackupControl { } } DatabaseContainer.reset() - selection + restoredSelection } if (DatabaseContainer.ready) { DatabaseContainer.instance - afterRestore(selection) + afterRestore(restoredSelection) } hourglass.dismiss() Log.i(TAG, "Restored database successfully")