android: stricter IO usage
This commit is contained in:
parent
888c5e567f
commit
9b90c7ba84
7 changed files with 76 additions and 54 deletions
|
@ -31,6 +31,18 @@ class MainActivity : FlutterActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
Log.i(LOG_TAG, "onCreate intent=$intent")
|
Log.i(LOG_TAG, "onCreate intent=$intent")
|
||||||
|
// StrictMode.setThreadPolicy(
|
||||||
|
// StrictMode.ThreadPolicy.Builder()
|
||||||
|
// .detectAll()
|
||||||
|
// .penaltyLog()
|
||||||
|
// .build()
|
||||||
|
// )
|
||||||
|
// StrictMode.setVmPolicy(
|
||||||
|
// StrictMode.VmPolicy.Builder()
|
||||||
|
// .detectAll()
|
||||||
|
// .penaltyLog()
|
||||||
|
// .build()
|
||||||
|
// )
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val messenger = flutterEngine!!.dartExecutor.binaryMessenger
|
val messenger = flutterEngine!!.dartExecutor.binaryMessenger
|
||||||
|
|
|
@ -36,7 +36,7 @@ import java.util.*
|
||||||
class DebugHandler(private val context: Context) : MethodCallHandler {
|
class DebugHandler(private val context: Context) : MethodCallHandler {
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"getContextDirs" -> result.success(getContextDirs())
|
"getContextDirs" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getContextDirs) }
|
||||||
"getEnv" -> result.success(System.getenv())
|
"getEnv" -> result.success(System.getenv())
|
||||||
"getBitmapFactoryInfo" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getBitmapFactoryInfo) }
|
"getBitmapFactoryInfo" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getBitmapFactoryInfo) }
|
||||||
"getContentResolverMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getContentResolverMetadata) }
|
"getContentResolverMetadata" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getContentResolverMetadata) }
|
||||||
|
@ -48,24 +48,28 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getContextDirs() = hashMapOf(
|
private fun getContextDirs(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
||||||
"cacheDir" to context.cacheDir,
|
val dirs = hashMapOf(
|
||||||
"filesDir" to context.filesDir,
|
"cacheDir" to context.cacheDir,
|
||||||
"obbDir" to context.obbDir,
|
"filesDir" to context.filesDir,
|
||||||
"externalCacheDir" to context.externalCacheDir,
|
"obbDir" to context.obbDir,
|
||||||
).apply {
|
"externalCacheDir" to context.externalCacheDir,
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
).apply {
|
||||||
putAll(
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
hashMapOf(
|
putAll(
|
||||||
"codeCacheDir" to context.codeCacheDir,
|
hashMapOf(
|
||||||
"noBackupFilesDir" to context.noBackupFilesDir,
|
"codeCacheDir" to context.codeCacheDir,
|
||||||
|
"noBackupFilesDir" to context.noBackupFilesDir,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
put("dataDir", context.dataDir)
|
||||||
put("dataDir", context.dataDir)
|
}
|
||||||
}
|
}.mapValues { it.value?.path }
|
||||||
}.mapValues { it.value?.path }
|
|
||||||
|
result.success(dirs)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getBitmapFactoryInfo(call: MethodCall, result: MethodChannel.Result) {
|
private fun getBitmapFactoryInfo(call: MethodCall, result: MethodChannel.Result) {
|
||||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||||
|
|
|
@ -9,11 +9,14 @@ import deckers.thibault.aves.utils.LogUtils
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class GlobalSearchHandler(private val context: Activity) : MethodCallHandler {
|
class GlobalSearchHandler(private val context: Activity) : MethodCallHandler {
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"registerCallback" -> safe(call, result, ::registerCallback)
|
"registerCallback" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::registerCallback) }
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +28,6 @@ class GlobalSearchHandler(private val context: Activity) : MethodCallHandler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i(LOG_TAG, "register global search callback")
|
|
||||||
context.getSharedPreferences(SearchSuggestionsProvider.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
context.getSharedPreferences(SearchSuggestionsProvider.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
||||||
.edit()
|
.edit()
|
||||||
.putLong(SearchSuggestionsProvider.CALLBACK_HANDLE_KEY, callbackHandle)
|
.putLong(SearchSuggestionsProvider.CALLBACK_HANDLE_KEY, callbackHandle)
|
||||||
|
|
|
@ -23,13 +23,13 @@ import java.util.*
|
||||||
class StorageHandler(private val context: Context) : MethodCallHandler {
|
class StorageHandler(private val context: Context) : MethodCallHandler {
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"getStorageVolumes" -> safe(call, result, ::getStorageVolumes)
|
"getStorageVolumes" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getStorageVolumes) }
|
||||||
"getFreeSpace" -> safe(call, result, ::getFreeSpace)
|
"getFreeSpace" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getFreeSpace) }
|
||||||
"getGrantedDirectories" -> safe(call, result, ::getGrantedDirectories)
|
"getGrantedDirectories" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getGrantedDirectories) }
|
||||||
"getInaccessibleDirectories" -> safe(call, result, ::getInaccessibleDirectories)
|
"getInaccessibleDirectories" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getInaccessibleDirectories) }
|
||||||
"getRestrictedDirectories" -> safe(call, result, ::getRestrictedDirectories)
|
"getRestrictedDirectories" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getRestrictedDirectories) }
|
||||||
"revokeDirectoryAccess" -> safe(call, result, ::revokeDirectoryAccess)
|
"revokeDirectoryAccess" -> safe(call, result, ::revokeDirectoryAccess)
|
||||||
"deleteEmptyDirectories" -> safe(call, result, ::deleteEmptyDirectories)
|
"deleteEmptyDirectories" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::deleteEmptyDirectories) }
|
||||||
"scanFile" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::scanFile) }
|
"scanFile" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::scanFile) }
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
|
||||||
private fun getStorageVolumes(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
private fun getStorageVolumes(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
||||||
val volumes = ArrayList<Map<String, Any>>()
|
val volumes = ArrayList<Map<String, Any>>()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
val sm = context.getSystemService(StorageManager::class.java)
|
val sm = context.getSystemService(Context.STORAGE_SERVICE) as? StorageManager
|
||||||
if (sm != null) {
|
if (sm != null) {
|
||||||
for (volumePath in getVolumePaths(context)) {
|
for (volumePath in getVolumePaths(context)) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -39,7 +39,7 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any?
|
||||||
handler = Handler(Looper.getMainLooper())
|
handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
when (op) {
|
when (op) {
|
||||||
"requestVolumeAccess" -> requestVolumeAccess()
|
"requestVolumeAccess" -> GlobalScope.launch(Dispatchers.IO) { requestVolumeAccess() }
|
||||||
"createFile" -> GlobalScope.launch(Dispatchers.IO) { createFile() }
|
"createFile" -> GlobalScope.launch(Dispatchers.IO) { createFile() }
|
||||||
"openFile" -> GlobalScope.launch(Dispatchers.IO) { openFile() }
|
"openFile" -> GlobalScope.launch(Dispatchers.IO) { openFile() }
|
||||||
"selectDirectory" -> GlobalScope.launch(Dispatchers.IO) { selectDirectory() }
|
"selectDirectory" -> GlobalScope.launch(Dispatchers.IO) { selectDirectory() }
|
||||||
|
@ -83,18 +83,20 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any?
|
||||||
putExtra(Intent.EXTRA_TITLE, name)
|
putExtra(Intent.EXTRA_TITLE, name)
|
||||||
}
|
}
|
||||||
MainActivity.pendingResultHandlers[MainActivity.CREATE_FILE_REQUEST] = PendingResultHandler(null, { uri ->
|
MainActivity.pendingResultHandlers[MainActivity.CREATE_FILE_REQUEST] = PendingResultHandler(null, { uri ->
|
||||||
try {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
activity.contentResolver.openOutputStream(uri)?.use { output ->
|
try {
|
||||||
output as FileOutputStream
|
activity.contentResolver.openOutputStream(uri)?.use { output ->
|
||||||
// truncate is necessary when overwriting a longer file
|
output as FileOutputStream
|
||||||
output.channel.truncate(0)
|
// truncate is necessary when overwriting a longer file
|
||||||
output.write(bytes)
|
output.channel.truncate(0)
|
||||||
|
output.write(bytes)
|
||||||
|
}
|
||||||
|
success(true)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
error("createFile-write", "failed to write file at uri=$uri", e.message)
|
||||||
}
|
}
|
||||||
success(true)
|
endOfStream()
|
||||||
} catch (e: Exception) {
|
|
||||||
error("createFile-write", "failed to write file at uri=$uri", e.message)
|
|
||||||
}
|
}
|
||||||
endOfStream()
|
|
||||||
}, {
|
}, {
|
||||||
success(null)
|
success(null)
|
||||||
endOfStream()
|
endOfStream()
|
||||||
|
@ -115,13 +117,15 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any?
|
||||||
type = mimeType
|
type = mimeType
|
||||||
}
|
}
|
||||||
MainActivity.pendingResultHandlers[MainActivity.OPEN_FILE_REQUEST] = PendingResultHandler(null, { uri ->
|
MainActivity.pendingResultHandlers[MainActivity.OPEN_FILE_REQUEST] = PendingResultHandler(null, { uri ->
|
||||||
activity.contentResolver.openInputStream(uri)?.use { input ->
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
val buffer = ByteArray(BUFFER_SIZE)
|
activity.contentResolver.openInputStream(uri)?.use { input ->
|
||||||
var len: Int
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
while (input.read(buffer).also { len = it } != -1) {
|
var len: Int
|
||||||
success(buffer.copyOf(len))
|
while (input.read(buffer).also { len = it } != -1) {
|
||||||
|
success(buffer.copyOf(len))
|
||||||
|
}
|
||||||
|
endOfStream()
|
||||||
}
|
}
|
||||||
endOfStream()
|
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
success(ByteArray(0))
|
success(ByteArray(0))
|
||||||
|
|
|
@ -25,7 +25,7 @@ object PermissionManager {
|
||||||
|
|
||||||
var intent: Intent? = null
|
var intent: Intent? = null
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
val sm = activity.getSystemService(StorageManager::class.java)
|
val sm = activity.getSystemService(Context.STORAGE_SERVICE) as? StorageManager
|
||||||
intent = sm?.getStorageVolume(File(path))?.createOpenDocumentTreeIntent()
|
intent = sm?.getStorageVolume(File(path))?.createOpenDocumentTreeIntent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,14 +187,13 @@ object StorageUtils {
|
||||||
// /storage/10F9-3F13/Pictures/ -> 10F9-3F13
|
// /storage/10F9-3F13/Pictures/ -> 10F9-3F13
|
||||||
private fun getVolumeUuidForTreeUri(context: Context, anyPath: String): String? {
|
private fun getVolumeUuidForTreeUri(context: Context, anyPath: String): String? {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
context.getSystemService(StorageManager::class.java)?.let { sm ->
|
val sm = context.getSystemService(Context.STORAGE_SERVICE) as? StorageManager
|
||||||
sm.getStorageVolume(File(anyPath))?.let { volume ->
|
sm?.getStorageVolume(File(anyPath))?.let { volume ->
|
||||||
if (volume.isPrimary) {
|
if (volume.isPrimary) {
|
||||||
return "primary"
|
return "primary"
|
||||||
}
|
}
|
||||||
volume.uuid?.let { uuid ->
|
volume.uuid?.let { uuid ->
|
||||||
return uuid.uppercase(Locale.ROOT)
|
return uuid.uppercase(Locale.ROOT)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,7 +221,8 @@ object StorageUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
context.getSystemService(StorageManager::class.java)?.let { sm ->
|
val sm = context.getSystemService(Context.STORAGE_SERVICE) as? StorageManager
|
||||||
|
if (sm != null) {
|
||||||
for (volumePath in getVolumePaths(context)) {
|
for (volumePath in getVolumePaths(context)) {
|
||||||
try {
|
try {
|
||||||
val volume = sm.getStorageVolume(File(volumePath))
|
val volume = sm.getStorageVolume(File(volumePath))
|
||||||
|
|
Loading…
Reference in a new issue