Skip to content

Commit 112a3ce

Browse files
authored
Merge pull request #793 from Crustack/fix/attachments-public-folder
Move attachment files according to dataInPublicFolder setting
2 parents c1c87c1 + f95a513 commit 112a3ce

35 files changed

+598
-166
lines changed

TRANSLATIONS.md

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,34 @@ See [Android Translations Converter](https://github.com/Crustack/android-transla
1919
<!-- translations:start -->
2020
| Language | Coverage |
2121
|----------|----------|
22-
| 🇺🇸 English | 100% (316/316) |
23-
| 🇪🇸 Catalan | 20% (65/316) |
24-
| 🇨🇿 Czech | 99% (313/316) |
25-
| 🇩🇰 Danish | 21% (69/316) |
26-
| 🇩🇪 German | 99% (313/316) |
27-
| 🇬🇷 Greek | 22% (72/316) |
28-
| 🇪🇸 Spanish | 99% (313/316) |
29-
| 🇫🇷 French | 95% (301/316) |
30-
| 🇭🇺 Hungarian | 20% (65/316) |
31-
| 🇮🇩 Indonesian | 23% (75/316) |
32-
| 🇮🇹 Italian | 92% (291/316) |
33-
| 🇯🇵 Japanese | 23% (73/316) |
34-
| 🇲🇲 Burmese | 28% (90/316) |
35-
| 🇳🇴 Norwegian Bokmål | 33% (106/316) |
36-
| 🇳🇱 Dutch | 67% (212/316) |
37-
| 🇳🇴 Norwegian Nynorsk | 33% (106/316) |
38-
| 🇵🇱 Polish | 94% (300/316) |
39-
| 🇧🇷 Portuguese (Brazil) | 98% (312/316) |
40-
| 🇵🇹 Portuguese (Portugal) | 22% (71/316) |
41-
| 🇷🇴 Romanian | 95% (301/316) |
42-
| 🇷🇺 Russian | 96% (305/316) |
43-
| 🇸🇰 Slovak | 20% (65/316) |
44-
| 🇸🇮 Slovenian | 34% (109/316) |
45-
| 🇸🇪 Swedish | 19% (63/316) |
46-
| 🇵🇭 Tagalog | 20% (65/316) |
47-
| 🇹🇷 Turkish | 23% (73/316) |
48-
| 🇺🇦 Ukrainian | 99% (314/316) |
49-
| 🇻🇳 Vietnamese | 33% (107/316) |
50-
| 🇨🇳 Chinese (Simplified) | 98% (312/316) |
51-
| 🇹🇼 Chinese (Traditional) | 93% (294/316) |
22+
| 🇺🇸 English | 100% (320/320) |
23+
| 🇪🇸 Catalan | 20% (65/320) |
24+
| 🇨🇿 Czech | 97% (313/320) |
25+
| 🇩🇰 Danish | 21% (69/320) |
26+
| 🇩🇪 German | 97% (313/320) |
27+
| 🇬🇷 Greek | 22% (72/320) |
28+
| 🇪🇸 Spanish | 97% (313/320) |
29+
| 🇫🇷 French | 94% (301/320) |
30+
| 🇭🇺 Hungarian | 20% (65/320) |
31+
| 🇮🇩 Indonesian | 23% (75/320) |
32+
| 🇮🇹 Italian | 90% (291/320) |
33+
| 🇯🇵 Japanese | 22% (73/320) |
34+
| 🇲🇲 Burmese | 28% (90/320) |
35+
| 🇳🇴 Norwegian Bokmål | 33% (106/320) |
36+
| 🇳🇱 Dutch | 66% (212/320) |
37+
| 🇳🇴 Norwegian Nynorsk | 33% (106/320) |
38+
| 🇵🇱 Polish | 93% (300/320) |
39+
| 🇧🇷 Portuguese (Brazil) | 97% (312/320) |
40+
| 🇵🇹 Portuguese (Portugal) | 22% (71/320) |
41+
| 🇷🇴 Romanian | 94% (301/320) |
42+
| 🇷🇺 Russian | 95% (305/320) |
43+
| 🇸🇰 Slovak | 20% (65/320) |
44+
| 🇸🇮 Slovenian | 34% (109/320) |
45+
| 🇸🇪 Swedish | 19% (63/320) |
46+
| 🇵🇭 Tagalog | 20% (65/320) |
47+
| 🇹🇷 Turkish | 22% (73/320) |
48+
| 🇺🇦 Ukrainian | 98% (314/320) |
49+
| 🇻🇳 Vietnamese | 33% (107/320) |
50+
| 🇨🇳 Chinese (Simplified) | 97% (312/320) |
51+
| 🇹🇼 Chinese (Traditional) | 91% (294/320) |
5252
<!-- translations:end -->

app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,14 @@
191191
android:exported="false"
192192
android:foregroundServiceType="mediaPlayback" />
193193

194+
<receiver
195+
android:name=".utils.backup.CleanupMissingAttachmentsReceiver"
196+
android:exported="false">
197+
<intent-filter>
198+
<action android:name="com.philkes.notallyx.action.CLEANUP_MISSING_ATTACHMENTS" />
199+
</intent-filter>
200+
</receiver>
201+
194202
</application>
195203

196204
</manifest>

app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import com.philkes.notallyx.utils.backup.isEqualTo
3030
import com.philkes.notallyx.utils.backup.modifiedNoteBackupExists
3131
import com.philkes.notallyx.utils.backup.scheduleAutoBackup
3232
import com.philkes.notallyx.utils.backup.updateAutoBackup
33+
import com.philkes.notallyx.utils.checkForMigrations
3334
import com.philkes.notallyx.utils.observeOnce
3435
import com.philkes.notallyx.utils.security.UnlockReceiver
3536
import java.util.concurrent.TimeUnit
@@ -52,6 +53,7 @@ class NotallyXApplication : Application(), Application.ActivityLifecycleCallback
5253
registerActivityLifecycleCallbacks(this)
5354
if (isTestRunner()) return
5455
preferences = NotallyXPreferences.getInstance(this)
56+
checkForMigrations()
5557
if (preferences.useDynamicColors.value) {
5658
if (DynamicColors.isDynamicColorAvailable()) {
5759
DynamicColors.applyToActivitiesIfAvailable(this)

app/src/main/java/com/philkes/notallyx/presentation/activity/note/PickNoteActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import com.philkes.notallyx.presentation.view.misc.ItemListener
2222
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
2323
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
2424
import com.philkes.notallyx.presentation.viewmodel.preference.NotesView
25-
import com.philkes.notallyx.utils.getExternalImagesDirectory
25+
import com.philkes.notallyx.utils.getCurrentImagesDirectory
2626
import java.util.Collections
2727
import kotlinx.coroutines.Dispatchers
2828
import kotlinx.coroutines.launch
@@ -59,7 +59,7 @@ open class PickNoteActivity : LockedActivity<ActivityPickNoteBinding>(), ItemLis
5959
labelTagsHiddenInOverview.value,
6060
imagesHiddenInOverview.value,
6161
),
62-
application.getExternalImagesDirectory(),
62+
application.getCurrentImagesDirectory(),
6363
this@PickNoteActivity,
6464
)
6565
}

app/src/main/java/com/philkes/notallyx/presentation/activity/note/PlayAudioActivity.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ import com.philkes.notallyx.presentation.activity.LockedActivity
2323
import com.philkes.notallyx.presentation.add
2424
import com.philkes.notallyx.presentation.dp
2525
import com.philkes.notallyx.presentation.setCancelButton
26-
import com.philkes.notallyx.presentation.view.note.audio.AudioControlView
2726
import com.philkes.notallyx.utils.audio.AudioPlayService
2827
import com.philkes.notallyx.utils.audio.LocalBinder
29-
import com.philkes.notallyx.utils.getExternalAudioDirectory
28+
import com.philkes.notallyx.utils.getCurrentAudioDirectory
3029
import com.philkes.notallyx.utils.getUriForFile
3130
import com.philkes.notallyx.utils.wrapWithChooser
3231
import java.io.File
@@ -180,7 +179,7 @@ class PlayAudioActivity : LockedActivity<ActivityPlayAudioBinding>() {
180179
}
181180

182181
private fun share() {
183-
val audioRoot = application.getExternalAudioDirectory()
182+
val audioRoot = application.getCurrentAudioDirectory()
184183
val file = if (audioRoot != null) File(audioRoot, audio.name) else null
185184
if (file != null && file.exists()) {
186185
val uri = getUriForFile(file)
@@ -209,7 +208,7 @@ class PlayAudioActivity : LockedActivity<ActivityPlayAudioBinding>() {
209208
}
210209

211210
private fun saveToDevice() {
212-
val audioRoot = application.getExternalAudioDirectory()
211+
val audioRoot = application.getCurrentAudioDirectory()
213212
val file = if (audioRoot != null) File(audioRoot, audio.name) else null
214213
if (file != null && file.exists()) {
215214
val intent =
@@ -231,7 +230,7 @@ class PlayAudioActivity : LockedActivity<ActivityPlayAudioBinding>() {
231230
private fun writeAudioToUri(uri: Uri) {
232231
lifecycleScope.launch {
233232
withContext(Dispatchers.IO) {
234-
val audioRoot = application.getExternalAudioDirectory()
233+
val audioRoot = application.getCurrentAudioDirectory()
235234
val file = if (audioRoot != null) File(audioRoot, audio.name) else null
236235
if (file != null && file.exists()) {
237236
val output = contentResolver.openOutputStream(uri) as FileOutputStream

app/src/main/java/com/philkes/notallyx/presentation/activity/note/ViewImageActivity.kt

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ import com.philkes.notallyx.presentation.activity.note.EditActivity.Companion.EX
2727
import com.philkes.notallyx.presentation.add
2828
import com.philkes.notallyx.presentation.setCancelButton
2929
import com.philkes.notallyx.presentation.view.note.image.ImageAdapter
30-
import com.philkes.notallyx.utils.getExternalImagesDirectory
30+
import com.philkes.notallyx.utils.SUBFOLDER_IMAGES
31+
import com.philkes.notallyx.utils.getCurrentImagesDirectory
3132
import com.philkes.notallyx.utils.getUriForFile
33+
import com.philkes.notallyx.utils.resolveAttachmentFile
3234
import com.philkes.notallyx.utils.wrapWithChooser
33-
import java.io.File
3435
import java.io.FileInputStream
3536
import java.io.FileOutputStream
3637
import kotlinx.coroutines.Dispatchers
@@ -91,7 +92,7 @@ class ViewImageActivity : LockedActivity<ActivityViewImageBinding>() {
9192
val images = ArrayList<FileAttachment>(original.size)
9293
original.filterNotTo(images) { image -> deletedImages.contains(image) }
9394

94-
val mediaRoot = application.getExternalImagesDirectory()
95+
val mediaRoot = application.getCurrentImagesDirectory()
9596
val adapter = ImageAdapter(mediaRoot, images)
9697
binding.MainListView.adapter = adapter
9798
setupToolbar(binding, adapter)
@@ -192,8 +193,7 @@ class ViewImageActivity : LockedActivity<ActivityViewImageBinding>() {
192193
}
193194

194195
private fun share(image: FileAttachment) {
195-
val mediaRoot = application.getExternalImagesDirectory()
196-
val file = if (mediaRoot != null) File(mediaRoot, image.localName) else null
196+
val file = application.resolveAttachmentFile(SUBFOLDER_IMAGES, image.localName)
197197
if (file != null && file.exists()) {
198198
val uri = getUriForFile(file)
199199
val intent =
@@ -214,8 +214,7 @@ class ViewImageActivity : LockedActivity<ActivityViewImageBinding>() {
214214
}
215215

216216
private fun saveToDevice(image: FileAttachment) {
217-
val mediaRoot = application.getExternalImagesDirectory()
218-
val file = if (mediaRoot != null) File(mediaRoot, image.localName) else null
217+
val file = application.resolveAttachmentFile(SUBFOLDER_IMAGES, image.localName)
219218
if (file != null && file.exists()) {
220219
val intent =
221220
Intent(Intent.ACTION_CREATE_DOCUMENT)
@@ -233,14 +232,8 @@ class ViewImageActivity : LockedActivity<ActivityViewImageBinding>() {
233232
private fun writeImageToUri(uri: Uri) {
234233
lifecycleScope.launch {
235234
withContext(Dispatchers.IO) {
236-
val mediaRoot = application.getExternalImagesDirectory()
237-
val file =
238-
if (mediaRoot != null)
239-
File(
240-
mediaRoot,
241-
requireNotNull(currentImage, { "currentImage is null" }).localName,
242-
)
243-
else null
235+
val ci = requireNotNull(currentImage) { "currentImage is null" }
236+
val file = application.resolveAttachmentFile(SUBFOLDER_IMAGES, ci.localName)
244237
if (file != null && file.exists()) {
245238
val output = contentResolver.openOutputStream(uri) as FileOutputStream
246239
output.channel.truncate(0)

app/src/main/java/com/philkes/notallyx/presentation/view/main/BaseNoteVH.kt

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -290,42 +290,45 @@ class BaseNoteVH(
290290
private fun setImages(images: List<FileAttachment>, mediaRoot: File?) {
291291
binding.apply {
292292
if (images.isNotEmpty() && !preferences.hideImages) {
293-
ImageView.visibility = VISIBLE
294293
Message.visibility = GONE
295-
296294
val image = images[0]
297295
val file = if (mediaRoot != null) File(mediaRoot, image.localName) else null
296+
if (file?.exists() == true) {
297+
ImageView.visibility = VISIBLE
298+
Glide.with(ImageView)
299+
.load(file)
300+
.centerCrop()
301+
.transition(DrawableTransitionOptions.withCrossFade())
302+
.diskCacheStrategy(DiskCacheStrategy.NONE)
303+
.listener(
304+
object : RequestListener<Drawable> {
305+
306+
override fun onLoadFailed(
307+
e: GlideException?,
308+
model: Any?,
309+
target: Target<Drawable>?,
310+
isFirstResource: Boolean,
311+
): Boolean {
312+
Message.visibility = VISIBLE
313+
return false
314+
}
298315

299-
Glide.with(ImageView)
300-
.load(file)
301-
.centerCrop()
302-
.transition(DrawableTransitionOptions.withCrossFade())
303-
.diskCacheStrategy(DiskCacheStrategy.NONE)
304-
.listener(
305-
object : RequestListener<Drawable> {
306-
307-
override fun onLoadFailed(
308-
e: GlideException?,
309-
model: Any?,
310-
target: Target<Drawable>?,
311-
isFirstResource: Boolean,
312-
): Boolean {
313-
Message.visibility = VISIBLE
314-
return false
315-
}
316-
317-
override fun onResourceReady(
318-
resource: Drawable?,
319-
model: Any?,
320-
target: Target<Drawable>?,
321-
dataSource: DataSource?,
322-
isFirstResource: Boolean,
323-
): Boolean {
324-
return false
316+
override fun onResourceReady(
317+
resource: Drawable?,
318+
model: Any?,
319+
target: Target<Drawable>?,
320+
dataSource: DataSource?,
321+
isFirstResource: Boolean,
322+
): Boolean {
323+
return false
324+
}
325325
}
326-
}
327-
)
328-
.into(ImageView)
326+
)
327+
.into(ImageView)
328+
} else {
329+
ImageView.visibility = GONE
330+
Message.visibility = VISIBLE
331+
}
329332
if (images.size > 1) {
330333
ImageViewMore.apply {
331334
text = images.size.toString()
@@ -335,7 +338,7 @@ class BaseNoteVH(
335338
ImageViewMore.visibility = GONE
336339
}
337340
} else {
338-
ImageView.visibility = GONE
341+
ImageLayout.visibility = GONE
339342
Message.visibility = GONE
340343
ImageViewMore.visibility = GONE
341344
Glide.with(ImageView).clear(ImageView)

app/src/main/java/com/philkes/notallyx/presentation/view/note/image/ImageVH.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class ImageVH(private val binding: RecyclerImageBinding) : RecyclerView.ViewHold
3030

3131
fun bind(file: File?) {
3232
binding.SSIV.recycle()
33-
if (file != null) {
33+
if (file?.exists() == true) {
3434
binding.Message.visibility = View.GONE
3535
val source = ImageSource.uri(Uri.fromFile(file))
3636
binding.SSIV.setImage(source)

app/src/main/java/com/philkes/notallyx/presentation/view/note/preview/PreviewImageVH.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class PreviewImageVH(
2323
}
2424

2525
fun bind(file: File?) {
26+
if (file?.exists() == false) {
27+
binding.Message.visibility = View.VISIBLE
28+
return
29+
}
2630
binding.Message.visibility = View.GONE
2731

2832
Glide.with(binding.ImageView)

app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,10 @@ import com.philkes.notallyx.utils.cancelNoteReminders
7777
import com.philkes.notallyx.utils.copyToLarge
7878
import com.philkes.notallyx.utils.deleteAttachments
7979
import com.philkes.notallyx.utils.getBackupDir
80-
import com.philkes.notallyx.utils.getExternalImagesDirectory
80+
import com.philkes.notallyx.utils.getCurrentImagesDirectory
8181
import com.philkes.notallyx.utils.getExternalMediaDirectory
8282
import com.philkes.notallyx.utils.log
83+
import com.philkes.notallyx.utils.migrateAllAttachments
8384
import com.philkes.notallyx.utils.scheduleNoteReminders
8485
import com.philkes.notallyx.utils.security.DecryptionException
8586
import com.philkes.notallyx.utils.security.EncryptionException
@@ -136,7 +137,8 @@ class BaseNoteModel(private val app: Application) : AndroidViewModel(app) {
136137

137138
val preferences = NotallyXPreferences.getInstance(app)
138139

139-
val imageRoot = app.getExternalImagesDirectory()
140+
val imageRoot
141+
get() = app.getCurrentImagesDirectory()
140142

141143
val importProgress = MutableLiveData<ImportProgress>()
142144
val progress = MutableLiveData<Progress>()
@@ -275,6 +277,7 @@ class BaseNoteModel(private val app: Application) : AndroidViewModel(app) {
275277
"Moving '${internalDatabaseFiles.map { it.name }}' to public '$targetDirectory' folder failed"
276278
)
277279
}
280+
app.migrateAllAttachments(toPrivate = false)
278281
}
279282
savePreference(preferences.dataInPublicFolder, true)
280283
callback?.invoke()
@@ -297,6 +300,7 @@ class BaseNoteModel(private val app: Application) : AndroidViewModel(app) {
297300
"Moving public '${externalDatabaseFiles.map { it.name }}' to internal '$targetDirectory' folder failed"
298301
)
299302
}
303+
app.migrateAllAttachments(toPrivate = true)
300304
}
301305
savePreference(preferences.dataInPublicFolder, false)
302306
callback?.invoke()

0 commit comments

Comments
 (0)