fixed selecting settings file to import on older devices
This commit is contained in:
parent
8cb88cc12c
commit
eb3a8f5626
11 changed files with 40 additions and 33 deletions
|
@ -155,7 +155,7 @@ class MainActivity : FlutterActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
@SuppressLint("WrongConstant", "ObsoleteSdkInt")
|
||||
private fun onDocumentTreeAccessResult(data: Intent?, resultCode: Int, requestCode: Int) {
|
||||
val treeUri = data?.data
|
||||
if (resultCode != RESULT_OK || treeUri == null) {
|
||||
|
|
|
@ -272,13 +272,13 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
|||
} else {
|
||||
var mimeType = "*/*"
|
||||
if (mimeTypes.size == 1) {
|
||||
// items have the same mime type & subtype
|
||||
// items have the same MIME type & subtype
|
||||
mimeType = mimeTypes.first()
|
||||
} else {
|
||||
// items have different subtypes
|
||||
val mimeTypeTypes = mimeTypes.map { it.split("/") }.distinct()
|
||||
if (mimeTypeTypes.size == 1) {
|
||||
// items have the same mime type
|
||||
// items have the same MIME type
|
||||
mimeType = "${mimeTypeTypes.first()}/*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -411,8 +411,8 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
|
|||
|
||||
// File type
|
||||
for (dir in metadata.getDirectoriesOfType(FileTypeDirectory::class.java)) {
|
||||
// * `metadata-extractor` sometimes detects the wrong mime type (e.g. `pef` file as `tiff`, `mpeg` as `dvd`)
|
||||
// * the content resolver / media store sometimes reports the wrong mime type (e.g. `png` file as `jpeg`, `tiff` as `srw`)
|
||||
// * `metadata-extractor` sometimes detects the wrong MIME type (e.g. `pef` file as `tiff`, `mpeg` as `dvd`)
|
||||
// * the content resolver / media store sometimes reports the wrong MIME type (e.g. `png` file as `jpeg`, `tiff` as `srw`)
|
||||
// * `context.getContentResolver().getType()` sometimes returns an incorrect value
|
||||
// * `MediaMetadataRetriever.setDataSource()` sometimes fails with `status = 0x80000000`
|
||||
// * file extension is unreliable
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package deckers.thibault.aves.channel.streams
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
|
@ -10,6 +11,7 @@ import android.util.Log
|
|||
import deckers.thibault.aves.MainActivity
|
||||
import deckers.thibault.aves.PendingStorageAccessResultHandler
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.PermissionManager
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.EventChannel.EventSink
|
||||
|
@ -90,9 +92,9 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any?
|
|||
endOfStream()
|
||||
}
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
private fun createFile() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
// TODO TLAD [<=API18] create file
|
||||
error("createFile-sdk", "unsupported SDK version=${Build.VERSION.SDK_INT}", null)
|
||||
return
|
||||
}
|
||||
|
@ -133,24 +135,16 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any?
|
|||
}
|
||||
|
||||
|
||||
private fun openFile() {
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
private suspend fun openFile() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
// TODO TLAD [<=API18] open file
|
||||
error("openFile-sdk", "unsupported SDK version=${Build.VERSION.SDK_INT}", null)
|
||||
return
|
||||
}
|
||||
|
||||
val mimeType = args["mimeType"] as String?
|
||||
if (mimeType == null) {
|
||||
error("openFile-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
val mimeType = args["mimeType"] as String? // optional
|
||||
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = mimeType
|
||||
}
|
||||
MainActivity.pendingStorageAccessResultHandlers[MainActivity.OPEN_FILE_REQUEST] = PendingStorageAccessResultHandler(null, { uri ->
|
||||
fun onGranted(uri: Uri) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
activity.contentResolver.openInputStream(uri)?.use { input ->
|
||||
val buffer = ByteArray(BUFFER_SIZE)
|
||||
|
@ -161,11 +155,24 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any?
|
|||
endOfStream()
|
||||
}
|
||||
}
|
||||
}, {
|
||||
}
|
||||
|
||||
fun onDenied() {
|
||||
success(ByteArray(0))
|
||||
endOfStream()
|
||||
})
|
||||
activity.startActivityForResult(intent, MainActivity.OPEN_FILE_REQUEST)
|
||||
}
|
||||
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
setTypeAndNormalize(mimeType ?: MimeTypes.ANY)
|
||||
}
|
||||
if (intent.resolveActivity(activity.packageManager) != null) {
|
||||
MainActivity.pendingStorageAccessResultHandlers[MainActivity.OPEN_FILE_REQUEST] = PendingStorageAccessResultHandler(null, ::onGranted, ::onDenied)
|
||||
activity.startActivityForResult(intent, MainActivity.OPEN_FILE_REQUEST)
|
||||
} else {
|
||||
MainActivity.notifyError("failed to resolve activity for intent=$intent")
|
||||
onDenied()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancel(arguments: Any?) {}
|
||||
|
|
|
@ -161,7 +161,7 @@ class SourceEntry {
|
|||
Metadata.openSafeInputStream(context, uri, sourceMimeType, sizeBytes)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input)
|
||||
|
||||
// do not switch on specific mime types, as the reported mime type could be wrong
|
||||
// do not switch on specific MIME types, as the reported MIME type could be wrong
|
||||
// (e.g. PNG registered as JPG)
|
||||
if (isVideo) {
|
||||
for (dir in metadata.getDirectoriesOfType(AviDirectory::class.java)) {
|
||||
|
|
|
@ -175,7 +175,7 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
// but for single items, `contentUri` already contains the ID
|
||||
val itemUri = if (contentUriContainsId) contentUri else ContentUris.withAppendedId(contentUri, contentId.toLong())
|
||||
// `mimeType` can be registered as null for file media URIs with unsupported media types (e.g. TIFF on old devices)
|
||||
// in that case we try to use the mime type provided along the URI
|
||||
// in that case we try to use the MIME type provided along the URI
|
||||
val mimeType: String? = cursor.getString(mimeTypeColumn) ?: fileMimeType
|
||||
val width = cursor.getInt(widthColumn)
|
||||
val height = cursor.getInt(heightColumn)
|
||||
|
|
|
@ -3,7 +3,7 @@ package deckers.thibault.aves.utils
|
|||
import androidx.exifinterface.media.ExifInterface
|
||||
|
||||
object MimeTypes {
|
||||
private const val IMAGE = "image"
|
||||
const val ANY = "*/*";
|
||||
|
||||
// generic raster
|
||||
const val BMP = "image/bmp"
|
||||
|
@ -45,8 +45,6 @@ object MimeTypes {
|
|||
// vector
|
||||
const val SVG = "image/svg+xml"
|
||||
|
||||
private const val VIDEO = "video"
|
||||
|
||||
private const val AVI = "video/avi"
|
||||
private const val AVI_VND = "video/vnd.avi"
|
||||
const val DVD = "video/dvd"
|
||||
|
@ -58,9 +56,9 @@ object MimeTypes {
|
|||
private const val OGV = "video/ogg"
|
||||
private const val WEBM = "video/webm"
|
||||
|
||||
fun isImage(mimeType: String?) = mimeType != null && mimeType.startsWith(IMAGE)
|
||||
fun isImage(mimeType: String?) = mimeType != null && mimeType.startsWith("image")
|
||||
|
||||
fun isVideo(mimeType: String?) = mimeType != null && mimeType.startsWith(VIDEO)
|
||||
fun isVideo(mimeType: String?) = mimeType != null && mimeType.startsWith("video")
|
||||
|
||||
fun isHeic(mimeType: String?) = mimeType != null && (mimeType == HEIC || mimeType == HEIF)
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ class PlatformAndroidAppService implements AndroidAppService {
|
|||
|
||||
@override
|
||||
Future<bool> shareEntries(Iterable<AvesEntry> entries) async {
|
||||
// loosen mime type to a generic one, so we can share with badly defined apps
|
||||
// loosen MIME type to a generic one, so we can share with badly defined apps
|
||||
// e.g. Google Lens declares receiving "image/jpeg" only, but it can actually handle more formats
|
||||
final urisByMimeType = groupBy<AvesEntry, String>(entries, (e) => e.mimeTypeAnySubtype).map((k, v) => MapEntry(k, v.map((e) => e.uri).toList()));
|
||||
try {
|
||||
|
|
|
@ -133,7 +133,7 @@ class AndroidDebugService {
|
|||
|
||||
static Future<Map> getMetadataExtractorSummary(AvesEntry entry) async {
|
||||
try {
|
||||
// returns map with the mime type and tag count for each directory found by `metadata-extractor`
|
||||
// returns map with the MIME type and tag count for each directory found by `metadata-extractor`
|
||||
final result = await platform.invokeMethod('getMetadataExtractorSummary', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
|
|
|
@ -36,7 +36,7 @@ abstract class StorageService {
|
|||
// return whether operation succeeded (`null` if user cancelled)
|
||||
Future<bool?> createFile(String name, String mimeType, Uint8List bytes);
|
||||
|
||||
Future<Uint8List> openFile(String mimeType);
|
||||
Future<Uint8List> openFile([String? mimeType]);
|
||||
}
|
||||
|
||||
class PlatformStorageService implements StorageService {
|
||||
|
@ -231,7 +231,7 @@ class PlatformStorageService implements StorageService {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> openFile(String mimeType) async {
|
||||
Future<Uint8List> openFile([String? mimeType]) async {
|
||||
try {
|
||||
final completer = Completer<Uint8List>.sync();
|
||||
final sink = OutputBuffer();
|
||||
|
|
|
@ -123,7 +123,9 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
|
|||
}
|
||||
break;
|
||||
case SettingsAction.import:
|
||||
final bytes = await storageService.openFile(MimeTypes.json);
|
||||
// specifying the JSON MIME type to restrict openable files is correct in theory,
|
||||
// but older devices (e.g. SM-P580, API 27) that do not recognize JSON files as such would filter them out
|
||||
final bytes = await storageService.openFile();
|
||||
if (bytes.isNotEmpty) {
|
||||
try {
|
||||
await settings.fromJson(utf8.decode(bytes));
|
||||
|
|
Loading…
Reference in a new issue