Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
deckerst committed Feb 22, 2024
2 parents 283a3eb + 87c8035 commit 579e7a2
Show file tree
Hide file tree
Showing 137 changed files with 11,181 additions and 605 deletions.
2 changes: 1 addition & 1 deletion .flutter
Submodule .flutter updated 2166 files
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone the repository.
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Get packages for the Flutter project.
run: scripts/pub_get_all.sh
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
java-version: '17'

- name: Clone the repository.
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Get packages for the Flutter project.
run: scripts/pub_get_all.sh
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.

## <a id="unreleased"></a>[Unreleased]

## <a id="v1.10.5"></a>[v1.10.5] - 2024-02-22

### Added

- Viewer: prompt to show newly edited item
- Widget: outline color options according to device theme
- Catalan translation (thanks Marc Amorós)

### Changed

- upgraded Flutter to stable v3.19.1

### Fixed

- untracked binned items recovery
- untracked vault items recovery

## <a id="v1.10.4"></a>[v1.10.4] - 2024-02-07

### Fixed
Expand Down
1 change: 0 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ dependencies {

implementation "androidx.appcompat:appcompat:1.6.1"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.exifinterface:exifinterface:1.3.7'
implementation 'androidx.lifecycle:lifecycle-process:2.7.0'
implementation 'androidx.media:media:1.7.0'
implementation 'androidx.multidex:multidex:2.0.1'
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<!-- as of Flutter v3.16.0 (stable),
<!-- as of Flutter v3.19.0 (stable),
Impeller fails to render videos & platform views, has poor performance -->
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class HomeWidgetProvider : AppWidgetProvider() {
val (widthPx, heightPx) = getWidgetSizePx(context, widgetInfo)
if (widthPx == 0 || heightPx == 0) return null

val isNightModeOn = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES

initFlutterEngine(context)
val messenger = flutterEngine!!.dartExecutor
val channel = MethodChannel(messenger, WIDGET_DRAW_CHANNEL)
Expand All @@ -101,6 +103,7 @@ class HomeWidgetProvider : AppWidgetProvider() {
"devicePixelRatio" to getDevicePixelRatio(),
"drawEntryImage" to drawEntryImage,
"reuseEntry" to reuseEntry,
"isSystemThemeDark" to isNightModeOn,
), object : MethodChannel.Result {
override fun success(result: Any?) {
cont.resume(result)
Expand Down
13 changes: 13 additions & 0 deletions android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import deckers.thibault.aves.channel.calls.*
import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler
import deckers.thibault.aves.channel.calls.window.WindowHandler
import deckers.thibault.aves.channel.streams.*
import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.FlutterUtils.enableSoftwareRendering
import deckers.thibault.aves.utils.FlutterUtils.isSoftwareRenderingRequired
import deckers.thibault.aves.utils.LogUtils
Expand Down Expand Up @@ -218,6 +219,7 @@ open class MainActivity : FlutterFragmentActivity() {
OPEN_FILE_REQUEST -> onStorageAccessResult(requestCode, data?.data)

PICK_COLLECTION_FILTERS_REQUEST -> onCollectionFiltersPickResult(resultCode, data)
EDIT_REQUEST -> onEditResult(resultCode, data)
}
}

Expand All @@ -226,6 +228,14 @@ open class MainActivity : FlutterFragmentActivity() {
pendingCollectionFilterPickHandler?.let { it(filters) }
}

private fun onEditResult(resultCode: Int, intent: Intent?) {
val fields: FieldMap? = if (resultCode == RESULT_OK) hashMapOf(
"uri" to intent?.data.toString(),
"mimeType" to intent?.type,
) else null
pendingEditIntentHandler?.let { it(fields) }
}

private fun onDocumentTreeAccessResult(requestCode: Int, resultCode: Int, intent: Intent?) {
val treeUri = intent?.data
if (resultCode != RESULT_OK || treeUri == null) {
Expand Down Expand Up @@ -458,6 +468,7 @@ open class MainActivity : FlutterFragmentActivity() {
const val DELETE_SINGLE_PERMISSION_REQUEST = 5
const val MEDIA_WRITE_BULK_PERMISSION_REQUEST = 6
const val PICK_COLLECTION_FILTERS_REQUEST = 7
const val EDIT_REQUEST = 8

const val INTENT_ACTION_EDIT = "edit"
const val INTENT_ACTION_PICK_ITEMS = "pick_items"
Expand Down Expand Up @@ -493,6 +504,8 @@ open class MainActivity : FlutterFragmentActivity() {

var pendingCollectionFilterPickHandler: ((filters: List<String>?) -> Unit)? = null

var pendingEditIntentHandler: ((fields: FieldMap?) -> Unit)? = null

private fun onStorageAccessResult(requestCode: Int, uri: Uri?) {
Log.i(LOG_TAG, "onStorageAccessResult with requestCode=$requestCode, uri=$uri")
val handler = pendingStorageAccessResultHandlers.remove(requestCode) ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
"getPackages" -> ioScope.launch { safe(call, result, ::getPackages) }
"getAppIcon" -> ioScope.launch { safeSuspend(call, result, ::getAppIcon) }
"copyToClipboard" -> ioScope.launch { safe(call, result, ::copyToClipboard) }
"edit" -> safe(call, result, ::edit)
"open" -> safe(call, result, ::open)
"openMap" -> safe(call, result, ::openMap)
"setAs" -> safe(call, result, ::setAs)
Expand Down Expand Up @@ -207,22 +206,6 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
}
}

private fun edit(call: MethodCall, result: MethodChannel.Result) {
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
val mimeType = call.argument<String>("mimeType")
if (uri == null) {
result.error("edit-args", "missing arguments", null)
return
}

val intent = Intent(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.setDataAndType(getShareableUri(context, uri), mimeType)
val started = safeStartActivity(intent)

result.success(started)
}

private fun open(call: MethodCall, result: MethodChannel.Result) {
val title = call.argument<String>("title")
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
Expand Down Expand Up @@ -404,6 +387,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
// on API 25, `String[]` or `ArrayList` extras are null when using the shortcut
// so we use a joined `String` as fallback
.putExtra(EXTRA_KEY_FILTERS_STRING, filters.joinToString(EXTRA_STRING_ARRAY_SEPARATOR))

else -> {
result.error("pin-intent", "failed to build intent", null)
return
Expand Down Expand Up @@ -434,6 +418,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
FileProvider.getUriForFile(context, authority, File(path))
}
}

else -> uri
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
when (call.method) {
"getDataUsage" -> ioScope.launch { safe(call, result, ::getDataUsage) }
"getStorageVolumes" -> ioScope.launch { safe(call, result, ::getStorageVolumes) }
"getUntrackedTrashPaths" -> ioScope.launch { safe(call, result, ::getUntrackedTrashPaths) }
"getUntrackedVaultPaths" -> ioScope.launch { safe(call, result, ::getUntrackedVaultPaths) }
"getVaultRoot" -> ioScope.launch { safe(call, result, ::getVaultRoot) }
"getFreeSpace" -> ioScope.launch { safe(call, result, ::getFreeSpace) }
"getGrantedDirectories" -> ioScope.launch { safe(call, result, ::getGrantedDirectories) }
Expand Down Expand Up @@ -125,6 +127,35 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
result.success(volumes)
}

private fun getUntrackedTrashPaths(call: MethodCall, result: MethodChannel.Result) {
val knownPaths = call.argument<List<String>>("knownPaths")
if (knownPaths == null) {
result.error("getUntrackedTrashPaths-args", "missing arguments", null)
return
}

val trashDirs = context.getExternalFilesDirs(null).mapNotNull { StorageUtils.trashDirFor(context, it.path) }
val trashItemPaths = trashDirs.flatMap { dir -> dir.listFiles()?.map { file -> file.path } ?: listOf() }
val untrackedPaths = trashItemPaths.filterNot(knownPaths::contains).toList()

result.success(untrackedPaths)
}

private fun getUntrackedVaultPaths(call: MethodCall, result: MethodChannel.Result) {
val vault = call.argument<String>("vault")
val knownPaths = call.argument<List<String>>("knownPaths")
if (vault == null || knownPaths == null) {
result.error("getUntrackedVaultPaths-args", "missing arguments", null)
return
}

val vaultDir = File(StorageUtils.getVaultRoot(context), vault)
val vaultItemPaths = vaultDir.listFiles()?.map { file -> file.path } ?: listOf()
val untrackedPaths = vaultItemPaths.filterNot(knownPaths::contains).toList()

result.success(untrackedPaths)
}

private fun getVaultRoot(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
result.success(StorageUtils.getVaultRoot(context))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.os.Looper
import android.util.Log
import deckers.thibault.aves.MainActivity
import deckers.thibault.aves.PendingStorageAccessResultHandler
import deckers.thibault.aves.channel.calls.AppAdapterHandler
import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.MimeTypes
import deckers.thibault.aves.utils.PermissionManager
Expand Down Expand Up @@ -47,6 +48,7 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
"requestMediaFileAccess" -> ioScope.launch { requestMediaFileAccess() }
"createFile" -> ioScope.launch { createFile() }
"openFile" -> ioScope.launch { openFile() }
"edit" -> edit()
"pickCollectionFilters" -> pickCollectionFilters()
else -> endOfStream()
}
Expand Down Expand Up @@ -100,10 +102,13 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
endOfStream()
}

private suspend fun safeStartActivityForResult(intent: Intent, requestCode: Int, onGranted: (uri: Uri) -> Unit, onDenied: () -> Unit) {
private suspend fun safeStartActivityForStorageAccessResult(intent: Intent, requestCode: Int, onGranted: (uri: Uri) -> Unit, onDenied: () -> Unit) {
if (intent.resolveActivity(activity.packageManager) != null) {
MainActivity.pendingStorageAccessResultHandlers[requestCode] = PendingStorageAccessResultHandler(null, onGranted, onDenied)
activity.startActivityForResult(intent, requestCode)
if (!safeStartActivityForResult(intent, requestCode)) {
MainActivity.notifyError("failed to start activity for intent=$intent extras=${intent.extras}")
onDenied()
}
} else {
MainActivity.notifyError("failed to resolve activity for intent=$intent extras=${intent.extras}")
onDenied()
Expand Down Expand Up @@ -144,7 +149,7 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
type = mimeType
putExtra(Intent.EXTRA_TITLE, name)
}
safeStartActivityForResult(intent, MainActivity.CREATE_FILE_REQUEST, ::onGranted, ::onDenied)
safeStartActivityForStorageAccessResult(intent, MainActivity.CREATE_FILE_REQUEST, ::onGranted, ::onDenied)
}

private suspend fun openFile() {
Expand Down Expand Up @@ -177,7 +182,33 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
addCategory(Intent.CATEGORY_OPENABLE)
setTypeAndNormalize(mimeType ?: MimeTypes.ANY)
}
safeStartActivityForResult(intent, MainActivity.OPEN_FILE_REQUEST, ::onGranted, ::onDenied)
safeStartActivityForStorageAccessResult(intent, MainActivity.OPEN_FILE_REQUEST, ::onGranted, ::onDenied)
}

private fun edit() {
val uri = args["uri"] as String?
val mimeType = args["mimeType"] as String? // optional
if (uri == null) {
error("edit-args", "missing arguments", null)
return
}

val intent = Intent(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.setDataAndType(AppAdapterHandler.getShareableUri(activity, Uri.parse(uri)), mimeType)

if (intent.resolveActivity(activity.packageManager) == null) {
error("edit-resolve", "cannot resolve activity for this intent", null)
return
}

MainActivity.pendingEditIntentHandler = { fields ->
success(fields)
endOfStream()
}
if (!safeStartActivityForResult(intent, MainActivity.EDIT_REQUEST)) {
error("edit-start", "cannot start activity for this intent", null)
}
}

private fun pickCollectionFilters() {
Expand All @@ -192,6 +223,24 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
activity.startActivityForResult(intent, MainActivity.PICK_COLLECTION_FILTERS_REQUEST)
}

private fun safeStartActivityForResult(intent: Intent, requestCode: Int): Boolean {
return try {
activity.startActivityForResult(intent, requestCode)
true
} catch (e: SecurityException) {
if (intent.flags and Intent.FLAG_GRANT_WRITE_URI_PERMISSION != 0) {
// in some environments, providing the write flag yields a `SecurityException`:
// "UID XXXX does not have permission to content://XXXX"
// so we retry without it
Log.i(LOG_TAG, "retry intent=$intent without FLAG_GRANT_WRITE_URI_PERMISSION")
intent.flags = intent.flags and Intent.FLAG_GRANT_WRITE_URI_PERMISSION.inv()
safeStartActivityForResult(intent, requestCode)
} else {
false
}
}
}

override fun onCancel(arguments: Any?) {
Log.i(LOG_TAG, "onCancel arguments=$arguments")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ object SafePngMetadataReader {
// Only compression method allowed by the spec is zero: deflate
if (compressionMethod.toInt() == 0) {
// bytes left for compressed text is:
// total bytes length - (profilenamebytes length + null byte + compression method byte)
// total bytes length - (profileNameBytes length + null byte + compression method byte)
val bytesLeft = bytes.size - (profileNameBytes.size + 1 + 1)
val compressedProfile = reader.getBytes(bytesLeft)
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,16 @@ internal class FileImageProvider : ImageProvider() {
var mimeType = sourceMimeType

if (mimeType == null) {
val extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
if (extension != null) {
var extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
if (extension.isEmpty()) {
uri.path?.let { path ->
val lastDotIndex = path.lastIndexOf('.')
if (lastDotIndex >= 0) {
extension = path.substring(lastDotIndex + 1)
}
}
}
if (extension.isNotEmpty()) {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
}
}
Expand Down
12 changes: 12 additions & 0 deletions android/app/src/main/res/values-ca/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Aves</string>
<string name="app_widget_label">Marc de foto</string>
<string name="wallpaper">Fons de pantalla</string>
<string name="safe_mode_shortcut_short_label">Mode segur</string>
<string name="search_shortcut_short_label">Buscar</string>
<string name="videos_shortcut_short_label">Vídeos</string>
<string name="analysis_channel_name">Exploració de mitjans</string>
<string name="analysis_notification_default_title">Explorant mitjans</string>
<string name="analysis_notification_action_stop">Atura</string>
</resources>
1 change: 1 addition & 0 deletions android/app/src/main/res/values-hi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
<string name="analysis_channel_name">मीडिया जाँचे</string>
<string name="app_name">ऐवीज</string>
<string name="videos_shortcut_short_label">वीडियो</string>
<string name="safe_mode_shortcut_short_label">सेफ मोड</string>
</resources>
1 change: 1 addition & 0 deletions android/exifinterface/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
Loading

0 comments on commit 579e7a2

Please sign in to comment.