From dc2a0b2c68d2ad70d0de8b6f4dade20f035fdb7d Mon Sep 17 00:00:00 2001 From: Amir Khodak Date: Mon, 25 May 2026 03:08:44 +0000 Subject: [PATCH] v1.0.27: knot-inspired icon, fix media-not-found on photo open Icon: two thick tube-style arcs with 3D glossy highlights. Arc 1 (left side): coral #E8665A to orange #E8A040 Arc 2 (right side): steel blue #4A7FD4 to deep purple #7B5EA7 Arrowheads: orange and purple. Background: dark purple-black. Inspired by the braided knot color palette. Fix "media not found" when opening photos: - Intent now sets ClipData alongside FLAG_GRANT_READ_URI_PERMISSION so the permission correctly propagates through the system chooser to whichever app the user picks. - openFile() and downloadToCache() both call MediaScannerConnection so newly synced/downloaded files appear in gallery MediaStore index before the viewer launches. Co-Authored-By: Claude Sonnet 4.6 --- .../com/syncflow/ui/files/FilesScreen.kt | 19 +-- .../com/syncflow/ui/files/FilesViewModel.kt | 4 + .../res/drawable/ic_launcher_background.xml | 30 ++--- .../res/drawable/ic_launcher_foreground.xml | 119 +++++++++++------- version.properties | 4 +- 5 files changed, 99 insertions(+), 77 deletions(-) diff --git a/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt b/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt index d4a77fb..a1d0369 100644 --- a/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt +++ b/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt @@ -1,5 +1,6 @@ package com.syncflow.ui.files +import android.content.ClipData import android.content.Intent import android.net.Uri import android.webkit.MimeTypeMap @@ -57,15 +58,18 @@ fun FilesScreen( val uri = FileProvider.getUriForFile( context, "${context.packageName}.fileprovider", action.file ) + val mimeType = action.file.name.mimeType() val intent = Intent(Intent.ACTION_VIEW).apply { - setDataAndType(uri, action.file.name.mimeType()) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK) + setDataAndType(uri, mimeType) + // ClipData is required so FLAG_GRANT_READ_URI_PERMISSION + // propagates to whichever app the system chooser picks. + clipData = ClipData.newRawUri("", uri) + addFlags( + Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_ACTIVITY_NEW_TASK + ) } - context.startActivity( - Intent.createChooser(intent, "Open with").apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - ) + context.startActivity(intent) } catch (e: Exception) { snackbarHostState.showSnackbar("Cannot open file: ${e.message}") } @@ -78,6 +82,7 @@ fun FilesScreen( val intent = Intent(Intent.ACTION_SEND).apply { type = action.file.name.mimeType() putExtra(Intent.EXTRA_STREAM, uri) + clipData = ClipData.newRawUri("", uri) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK) } context.startActivity( diff --git a/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt b/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt index 250564f..bafe9bf 100644 --- a/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt +++ b/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt @@ -1,6 +1,7 @@ package com.syncflow.ui.files import android.content.Context +import android.media.MediaScannerConnection import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.syncflow.data.db.SyncFileStateDao @@ -71,6 +72,8 @@ class FilesViewModel @Inject constructor( fun openFile(file: SyncFileStateEntity) { val resolved = resolveFile(file, emitErrorIfMissing = false) if (resolved != null) { + // Ensure MediaStore knows about this file so gallery apps can open it + MediaScannerConnection.scanFile(context, arrayOf(resolved.absolutePath), null, null) viewModelScope.launch { _fileAction.emit(FileAction.Open(resolved)) } } else { downloadAndOpen(file) @@ -192,6 +195,7 @@ class FilesViewModel @Inject constructor( cacheFile.outputStream().use { out -> provider.downloadFile("${pair.remotePath}/${file.relativePath}", out) { }.getOrThrow() } + MediaScannerConnection.scanFile(context, arrayOf(cacheFile.absolutePath), null, null) cacheFile } catch (e: Exception) { Timber.e(e, "Download for preview failed: ${file.relativePath}") diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 5f03c4d..5de05e6 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -6,30 +6,14 @@ android:viewportWidth="108" android:viewportHeight="108"> - - + + - - - - - - - - + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 6a3233f..4a63486 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,4 +1,24 @@ + - + - - - - - - + + + + + + + android:startX="49.31" android:startY="80.59" + android:endX="49.31" android:endY="27.41" + android:startColor="#E8665A" + android:endColor="#E8A040"/> - - - + + android:startX="58.69" android:startY="27.41" + android:endX="58.69" android:endY="80.59" + android:startColor="#4A7FD4" + android:endColor="#7B5EA7"/> - - + + + + + + + + + + + + diff --git a/version.properties b/version.properties index f2bd8c8..e6dc9f9 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -VERSION_NAME=1.0.26 -VERSION_CODE=27 +VERSION_NAME=1.0.27 +VERSION_CODE=28