diff --git a/TRANSLATIONS.md b/TRANSLATIONS.md
index d3aeef55..a85c3de2 100644
--- a/TRANSLATIONS.md
+++ b/TRANSLATIONS.md
@@ -19,34 +19,34 @@ See [Android Translations Converter](https://github.com/Crustack/android-transla
| Language | Coverage |
|----------|----------|
-| 🇺🇸 English | 100% (316/316) |
-| 🇪🇸 Catalan | 20% (65/316) |
-| 🇨🇿 Czech | 99% (313/316) |
-| 🇩🇰 Danish | 21% (69/316) |
-| 🇩🇪 German | 99% (313/316) |
-| 🇬🇷 Greek | 22% (72/316) |
-| 🇪🇸 Spanish | 99% (313/316) |
-| 🇫🇷 French | 95% (301/316) |
-| 🇭🇺 Hungarian | 20% (65/316) |
-| 🇮🇩 Indonesian | 23% (75/316) |
-| 🇮🇹 Italian | 92% (291/316) |
-| 🇯🇵 Japanese | 23% (73/316) |
-| 🇲🇲 Burmese | 28% (90/316) |
-| 🇳🇴 Norwegian Bokmål | 33% (106/316) |
-| 🇳🇱 Dutch | 67% (212/316) |
-| 🇳🇴 Norwegian Nynorsk | 33% (106/316) |
-| 🇵🇱 Polish | 94% (300/316) |
-| 🇧🇷 Portuguese (Brazil) | 98% (312/316) |
-| 🇵🇹 Portuguese (Portugal) | 22% (71/316) |
-| 🇷🇴 Romanian | 95% (301/316) |
-| 🇷🇺 Russian | 96% (305/316) |
-| 🇸🇰 Slovak | 20% (65/316) |
-| 🇸🇮 Slovenian | 34% (109/316) |
-| 🇸🇪 Swedish | 19% (63/316) |
-| 🇵🇭 Tagalog | 20% (65/316) |
-| 🇹🇷 Turkish | 23% (73/316) |
-| 🇺🇦 Ukrainian | 99% (314/316) |
-| 🇻🇳 Vietnamese | 33% (107/316) |
-| 🇨🇳 Chinese (Simplified) | 98% (312/316) |
-| 🇹🇼 Chinese (Traditional) | 93% (294/316) |
+| 🇺🇸 English | 100% (320/320) |
+| 🇪🇸 Catalan | 20% (65/320) |
+| 🇨🇿 Czech | 97% (313/320) |
+| 🇩🇰 Danish | 21% (69/320) |
+| 🇩🇪 German | 97% (313/320) |
+| 🇬🇷 Greek | 22% (72/320) |
+| 🇪🇸 Spanish | 97% (313/320) |
+| 🇫🇷 French | 94% (301/320) |
+| 🇭🇺 Hungarian | 20% (65/320) |
+| 🇮🇩 Indonesian | 23% (75/320) |
+| 🇮🇹 Italian | 90% (291/320) |
+| 🇯🇵 Japanese | 22% (73/320) |
+| 🇲🇲 Burmese | 28% (90/320) |
+| 🇳🇴 Norwegian Bokmål | 33% (106/320) |
+| 🇳🇱 Dutch | 66% (212/320) |
+| 🇳🇴 Norwegian Nynorsk | 33% (106/320) |
+| 🇵🇱 Polish | 93% (300/320) |
+| 🇧🇷 Portuguese (Brazil) | 97% (312/320) |
+| 🇵🇹 Portuguese (Portugal) | 22% (71/320) |
+| 🇷🇴 Romanian | 94% (301/320) |
+| 🇷🇺 Russian | 95% (305/320) |
+| 🇸🇰 Slovak | 20% (65/320) |
+| 🇸🇮 Slovenian | 34% (109/320) |
+| 🇸🇪 Swedish | 19% (63/320) |
+| 🇵🇭 Tagalog | 20% (65/320) |
+| 🇹🇷 Turkish | 22% (73/320) |
+| 🇺🇦 Ukrainian | 98% (314/320) |
+| 🇻🇳 Vietnamese | 33% (107/320) |
+| 🇨🇳 Chinese (Simplified) | 97% (312/320) |
+| 🇹🇼 Chinese (Traditional) | 91% (294/320) |
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8e3721d9..bbf32d67 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -191,6 +191,14 @@
android:exported="false"
android:foregroundServiceType="mediaPlayback" />
+
+
+
+
+
+
diff --git a/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt b/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt
index 95a5f331..fe7703e6 100644
--- a/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt
+++ b/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt
@@ -30,6 +30,7 @@ import com.philkes.notallyx.utils.backup.isEqualTo
import com.philkes.notallyx.utils.backup.modifiedNoteBackupExists
import com.philkes.notallyx.utils.backup.scheduleAutoBackup
import com.philkes.notallyx.utils.backup.updateAutoBackup
+import com.philkes.notallyx.utils.checkForMigrations
import com.philkes.notallyx.utils.observeOnce
import com.philkes.notallyx.utils.security.UnlockReceiver
import java.util.concurrent.TimeUnit
@@ -52,6 +53,7 @@ class NotallyXApplication : Application(), Application.ActivityLifecycleCallback
registerActivityLifecycleCallbacks(this)
if (isTestRunner()) return
preferences = NotallyXPreferences.getInstance(this)
+ checkForMigrations()
if (preferences.useDynamicColors.value) {
if (DynamicColors.isDynamicColorAvailable()) {
DynamicColors.applyToActivitiesIfAvailable(this)
diff --git a/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PickNoteActivity.kt b/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PickNoteActivity.kt
index 49b754d1..2c1d99fc 100644
--- a/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PickNoteActivity.kt
+++ b/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PickNoteActivity.kt
@@ -22,7 +22,7 @@ import com.philkes.notallyx.presentation.view.misc.ItemListener
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
import com.philkes.notallyx.presentation.viewmodel.preference.NotesView
-import com.philkes.notallyx.utils.getExternalImagesDirectory
+import com.philkes.notallyx.utils.getCurrentImagesDirectory
import java.util.Collections
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -59,7 +59,7 @@ open class PickNoteActivity : LockedActivity(), ItemLis
labelTagsHiddenInOverview.value,
imagesHiddenInOverview.value,
),
- application.getExternalImagesDirectory(),
+ application.getCurrentImagesDirectory(),
this@PickNoteActivity,
)
}
diff --git a/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PlayAudioActivity.kt b/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PlayAudioActivity.kt
index b72f94ee..bde515ef 100644
--- a/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PlayAudioActivity.kt
+++ b/app/src/main/java/com/philkes/notallyx/presentation/activity/note/PlayAudioActivity.kt
@@ -23,10 +23,9 @@ import com.philkes.notallyx.presentation.activity.LockedActivity
import com.philkes.notallyx.presentation.add
import com.philkes.notallyx.presentation.dp
import com.philkes.notallyx.presentation.setCancelButton
-import com.philkes.notallyx.presentation.view.note.audio.AudioControlView
import com.philkes.notallyx.utils.audio.AudioPlayService
import com.philkes.notallyx.utils.audio.LocalBinder
-import com.philkes.notallyx.utils.getExternalAudioDirectory
+import com.philkes.notallyx.utils.getCurrentAudioDirectory
import com.philkes.notallyx.utils.getUriForFile
import com.philkes.notallyx.utils.wrapWithChooser
import java.io.File
@@ -180,7 +179,7 @@ class PlayAudioActivity : LockedActivity() {
}
private fun share() {
- val audioRoot = application.getExternalAudioDirectory()
+ val audioRoot = application.getCurrentAudioDirectory()
val file = if (audioRoot != null) File(audioRoot, audio.name) else null
if (file != null && file.exists()) {
val uri = getUriForFile(file)
@@ -209,7 +208,7 @@ class PlayAudioActivity : LockedActivity() {
}
private fun saveToDevice() {
- val audioRoot = application.getExternalAudioDirectory()
+ val audioRoot = application.getCurrentAudioDirectory()
val file = if (audioRoot != null) File(audioRoot, audio.name) else null
if (file != null && file.exists()) {
val intent =
@@ -231,7 +230,7 @@ class PlayAudioActivity : LockedActivity() {
private fun writeAudioToUri(uri: Uri) {
lifecycleScope.launch {
withContext(Dispatchers.IO) {
- val audioRoot = application.getExternalAudioDirectory()
+ val audioRoot = application.getCurrentAudioDirectory()
val file = if (audioRoot != null) File(audioRoot, audio.name) else null
if (file != null && file.exists()) {
val output = contentResolver.openOutputStream(uri) as FileOutputStream
diff --git a/app/src/main/java/com/philkes/notallyx/presentation/activity/note/ViewImageActivity.kt b/app/src/main/java/com/philkes/notallyx/presentation/activity/note/ViewImageActivity.kt
index 489be120..4cf6e430 100644
--- a/app/src/main/java/com/philkes/notallyx/presentation/activity/note/ViewImageActivity.kt
+++ b/app/src/main/java/com/philkes/notallyx/presentation/activity/note/ViewImageActivity.kt
@@ -27,10 +27,11 @@ import com.philkes.notallyx.presentation.activity.note.EditActivity.Companion.EX
import com.philkes.notallyx.presentation.add
import com.philkes.notallyx.presentation.setCancelButton
import com.philkes.notallyx.presentation.view.note.image.ImageAdapter
-import com.philkes.notallyx.utils.getExternalImagesDirectory
+import com.philkes.notallyx.utils.SUBFOLDER_IMAGES
+import com.philkes.notallyx.utils.getCurrentImagesDirectory
import com.philkes.notallyx.utils.getUriForFile
+import com.philkes.notallyx.utils.resolveAttachmentFile
import com.philkes.notallyx.utils.wrapWithChooser
-import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import kotlinx.coroutines.Dispatchers
@@ -91,7 +92,7 @@ class ViewImageActivity : LockedActivity() {
val images = ArrayList(original.size)
original.filterNotTo(images) { image -> deletedImages.contains(image) }
- val mediaRoot = application.getExternalImagesDirectory()
+ val mediaRoot = application.getCurrentImagesDirectory()
val adapter = ImageAdapter(mediaRoot, images)
binding.MainListView.adapter = adapter
setupToolbar(binding, adapter)
@@ -192,8 +193,7 @@ class ViewImageActivity : LockedActivity() {
}
private fun share(image: FileAttachment) {
- val mediaRoot = application.getExternalImagesDirectory()
- val file = if (mediaRoot != null) File(mediaRoot, image.localName) else null
+ val file = application.resolveAttachmentFile(SUBFOLDER_IMAGES, image.localName)
if (file != null && file.exists()) {
val uri = getUriForFile(file)
val intent =
@@ -214,8 +214,7 @@ class ViewImageActivity : LockedActivity() {
}
private fun saveToDevice(image: FileAttachment) {
- val mediaRoot = application.getExternalImagesDirectory()
- val file = if (mediaRoot != null) File(mediaRoot, image.localName) else null
+ val file = application.resolveAttachmentFile(SUBFOLDER_IMAGES, image.localName)
if (file != null && file.exists()) {
val intent =
Intent(Intent.ACTION_CREATE_DOCUMENT)
@@ -233,14 +232,8 @@ class ViewImageActivity : LockedActivity() {
private fun writeImageToUri(uri: Uri) {
lifecycleScope.launch {
withContext(Dispatchers.IO) {
- val mediaRoot = application.getExternalImagesDirectory()
- val file =
- if (mediaRoot != null)
- File(
- mediaRoot,
- requireNotNull(currentImage, { "currentImage is null" }).localName,
- )
- else null
+ val ci = requireNotNull(currentImage) { "currentImage is null" }
+ val file = application.resolveAttachmentFile(SUBFOLDER_IMAGES, ci.localName)
if (file != null && file.exists()) {
val output = contentResolver.openOutputStream(uri) as FileOutputStream
output.channel.truncate(0)
diff --git a/app/src/main/java/com/philkes/notallyx/presentation/view/main/BaseNoteVH.kt b/app/src/main/java/com/philkes/notallyx/presentation/view/main/BaseNoteVH.kt
index df90dec0..333b8e70 100644
--- a/app/src/main/java/com/philkes/notallyx/presentation/view/main/BaseNoteVH.kt
+++ b/app/src/main/java/com/philkes/notallyx/presentation/view/main/BaseNoteVH.kt
@@ -290,42 +290,45 @@ class BaseNoteVH(
private fun setImages(images: List, mediaRoot: File?) {
binding.apply {
if (images.isNotEmpty() && !preferences.hideImages) {
- ImageView.visibility = VISIBLE
Message.visibility = GONE
-
val image = images[0]
val file = if (mediaRoot != null) File(mediaRoot, image.localName) else null
+ if (file?.exists() == true) {
+ ImageView.visibility = VISIBLE
+ Glide.with(ImageView)
+ .load(file)
+ .centerCrop()
+ .transition(DrawableTransitionOptions.withCrossFade())
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .listener(
+ object : RequestListener {
+
+ override fun onLoadFailed(
+ e: GlideException?,
+ model: Any?,
+ target: Target?,
+ isFirstResource: Boolean,
+ ): Boolean {
+ Message.visibility = VISIBLE
+ return false
+ }
- Glide.with(ImageView)
- .load(file)
- .centerCrop()
- .transition(DrawableTransitionOptions.withCrossFade())
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .listener(
- object : RequestListener {
-
- override fun onLoadFailed(
- e: GlideException?,
- model: Any?,
- target: Target?,
- isFirstResource: Boolean,
- ): Boolean {
- Message.visibility = VISIBLE
- return false
- }
-
- override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
- target: Target?,
- dataSource: DataSource?,
- isFirstResource: Boolean,
- ): Boolean {
- return false
+ override fun onResourceReady(
+ resource: Drawable?,
+ model: Any?,
+ target: Target?,
+ dataSource: DataSource?,
+ isFirstResource: Boolean,
+ ): Boolean {
+ return false
+ }
}
- }
- )
- .into(ImageView)
+ )
+ .into(ImageView)
+ } else {
+ ImageView.visibility = GONE
+ Message.visibility = VISIBLE
+ }
if (images.size > 1) {
ImageViewMore.apply {
text = images.size.toString()
@@ -335,7 +338,7 @@ class BaseNoteVH(
ImageViewMore.visibility = GONE
}
} else {
- ImageView.visibility = GONE
+ ImageLayout.visibility = GONE
Message.visibility = GONE
ImageViewMore.visibility = GONE
Glide.with(ImageView).clear(ImageView)
diff --git a/app/src/main/java/com/philkes/notallyx/presentation/view/note/image/ImageVH.kt b/app/src/main/java/com/philkes/notallyx/presentation/view/note/image/ImageVH.kt
index ebe893d3..21ecb2a3 100644
--- a/app/src/main/java/com/philkes/notallyx/presentation/view/note/image/ImageVH.kt
+++ b/app/src/main/java/com/philkes/notallyx/presentation/view/note/image/ImageVH.kt
@@ -30,7 +30,7 @@ class ImageVH(private val binding: RecyclerImageBinding) : RecyclerView.ViewHold
fun bind(file: File?) {
binding.SSIV.recycle()
- if (file != null) {
+ if (file?.exists() == true) {
binding.Message.visibility = View.GONE
val source = ImageSource.uri(Uri.fromFile(file))
binding.SSIV.setImage(source)
diff --git a/app/src/main/java/com/philkes/notallyx/presentation/view/note/preview/PreviewImageVH.kt b/app/src/main/java/com/philkes/notallyx/presentation/view/note/preview/PreviewImageVH.kt
index bae99442..fc9ad21c 100644
--- a/app/src/main/java/com/philkes/notallyx/presentation/view/note/preview/PreviewImageVH.kt
+++ b/app/src/main/java/com/philkes/notallyx/presentation/view/note/preview/PreviewImageVH.kt
@@ -23,6 +23,10 @@ class PreviewImageVH(
}
fun bind(file: File?) {
+ if (file?.exists() == false) {
+ binding.Message.visibility = View.VISIBLE
+ return
+ }
binding.Message.visibility = View.GONE
Glide.with(binding.ImageView)
diff --git a/app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt b/app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt
index 6b24fe80..66bbaede 100644
--- a/app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt
+++ b/app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt
@@ -77,9 +77,10 @@ import com.philkes.notallyx.utils.cancelNoteReminders
import com.philkes.notallyx.utils.copyToLarge
import com.philkes.notallyx.utils.deleteAttachments
import com.philkes.notallyx.utils.getBackupDir
-import com.philkes.notallyx.utils.getExternalImagesDirectory
+import com.philkes.notallyx.utils.getCurrentImagesDirectory
import com.philkes.notallyx.utils.getExternalMediaDirectory
import com.philkes.notallyx.utils.log
+import com.philkes.notallyx.utils.migrateAllAttachments
import com.philkes.notallyx.utils.scheduleNoteReminders
import com.philkes.notallyx.utils.security.DecryptionException
import com.philkes.notallyx.utils.security.EncryptionException
@@ -136,7 +137,8 @@ class BaseNoteModel(private val app: Application) : AndroidViewModel(app) {
val preferences = NotallyXPreferences.getInstance(app)
- val imageRoot = app.getExternalImagesDirectory()
+ val imageRoot
+ get() = app.getCurrentImagesDirectory()
val importProgress = MutableLiveData()
val progress = MutableLiveData