Skip to content

Commit 7e4178c

Browse files
committed
Move attachments according to dataInPublic setting
1 parent 31a5764 commit 7e4178c

20 files changed

+508
-74
lines changed

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/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()

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ import com.philkes.notallyx.utils.backup.importFile
5050
import com.philkes.notallyx.utils.cancelNoteReminders
5151
import com.philkes.notallyx.utils.cancelReminder
5252
import com.philkes.notallyx.utils.deleteAttachments
53-
import com.philkes.notallyx.utils.getExternalAudioDirectory
54-
import com.philkes.notallyx.utils.getExternalFilesDirectory
55-
import com.philkes.notallyx.utils.getExternalImagesDirectory
53+
import com.philkes.notallyx.utils.getCurrentAudioDirectory
54+
import com.philkes.notallyx.utils.getCurrentFilesDirectory
55+
import com.philkes.notallyx.utils.getCurrentImagesDirectory
5656
import com.philkes.notallyx.utils.getTempAudioFile
5757
import com.philkes.notallyx.utils.scheduleReminder
5858
import java.io.File
@@ -99,9 +99,9 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
9999
val addingFiles = MutableLiveData<Progress>()
100100
val eventBus = MutableLiveData<Event<List<FileError>>>()
101101

102-
var imageRoot = app.getExternalImagesDirectory()
103-
var audioRoot = app.getExternalAudioDirectory()
104-
var filesRoot = app.getExternalFilesDirectory()
102+
var imageRoot = app.getCurrentImagesDirectory()
103+
var audioRoot = app.getCurrentAudioDirectory()
104+
var filesRoot = app.getCurrentFilesDirectory()
105105

106106
var originalNote: BaseNote? = null
107107

@@ -134,7 +134,7 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
134134
Regenerate because the directory may have been deleted between the time of activity creation
135135
and image addition
136136
*/
137-
imageRoot = app.getExternalImagesDirectory()
137+
imageRoot = app.getCurrentImagesDirectory()
138138
requireNotNull(imageRoot) { "imageRoot is null" }
139139
addFiles(uris, imageRoot!!, FileType.IMAGE)
140140
}
@@ -144,7 +144,7 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
144144
Regenerate because the directory may have been deleted between the time of activity creation
145145
and image addition
146146
*/
147-
filesRoot = app.getExternalFilesDirectory()
147+
filesRoot = app.getCurrentFilesDirectory()
148148
requireNotNull(filesRoot) { "filesRoot is null" }
149149
addFiles(uris, filesRoot!!, FileType.ANY)
150150
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,17 @@ class NotallyXPreferences private constructor(private val context: Context) {
155155
val dataInPublicFolder =
156156
BooleanPreference("dataOnExternalStorage", preferences, false, R.string.data_in_public)
157157

158+
/**
159+
* Tracks app-internal data schema/migration steps. 0 = initial state, no migrations run yet See
160+
* [DataSchemaMigrations.kt]
161+
*/
162+
val dataSchemaId = IntPreference("dataSchemaId", preferences, 0, 0, Integer.MAX_VALUE)
163+
164+
fun setDataSchemaId(value: Int) {
165+
preferences.edit(true) { putInt(dataSchemaId.key, value) }
166+
dataSchemaId.refresh()
167+
}
168+
158169
fun getWidgetData(id: Int) = preferences.getLong("widget:$id", 0)
159170

160171
fun getWidgetNoteType(id: Int) =

0 commit comments

Comments
 (0)