diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml
index 6dfd09831..96f60f539 100644
--- a/client/android/AndroidManifest.xml
+++ b/client/android/AndroidManifest.xml
@@ -91,6 +91,13 @@
android:exported="false"
android:theme="@style/Translucent" />
+
+
Открыть настройки уведомлений
Пожалуйста, установите приложение для просмотра файлов
+ Выберете файловый менеджер
\ No newline at end of file
diff --git a/client/android/res/values/strings.xml b/client/android/res/values/strings.xml
index bf8d76d1d..9a8ecce02 100644
--- a/client/android/res/values/strings.xml
+++ b/client/android/res/values/strings.xml
@@ -25,4 +25,5 @@
Open notification settings
Please install a file management utility to browse files
+ Choose a file browser
\ No newline at end of file
diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt
index 29a6d06ba..0dd3d3192 100644
--- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt
+++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt
@@ -520,21 +520,25 @@ class AmneziaActivity : QtActivity() {
type = "text/*"
putExtra(Intent.EXTRA_TITLE, fileName)
}.also {
- startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
- onSuccess = {
- it?.data?.let { uri ->
- Log.v(TAG, "Save file to $uri")
- try {
- contentResolver.openOutputStream(uri)?.use { os ->
- os.bufferedWriter().use { it.write(data) }
+ try {
+ startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
+ onSuccess = {
+ it?.data?.let { uri ->
+ Log.v(TAG, "Save file to $uri")
+ try {
+ contentResolver.openOutputStream(uri)?.use { os ->
+ os.bufferedWriter().use { it.write(data) }
+ }
+ } catch (e: IOException) {
+ Log.e(TAG, "Failed to save file $uri: $e")
+ // todo: send error to Qt
}
- } catch (e: IOException) {
- Log.e(TAG, "Failed to save file $uri: $e")
- // todo: send error to Qt
}
}
- }
- ))
+ ))
+ } catch (_: ActivityNotFoundException) {
+ Toast.makeText(this@AmneziaActivity, "Unsupported", Toast.LENGTH_LONG).show()
+ }
}
}
}
@@ -543,55 +547,73 @@ class AmneziaActivity : QtActivity() {
fun openFile(filter: String?) {
Log.v(TAG, "Open file with filter: $filter")
mainScope.launch {
- val mimeTypes = if (!filter.isNullOrEmpty()) {
- val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
- val mime = MimeTypeMap.getSingleton()
- extensionRegex.findAll(filter).map {
- it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
- }.toSet()
- } else emptySet()
-
- Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
- addCategory(Intent.CATEGORY_OPENABLE)
- Log.v(TAG, "File mimyType filter: $mimeTypes")
- if ("*/*" in mimeTypes) {
- type = "*/*"
- } else {
- when (mimeTypes.size) {
- 1 -> type = mimeTypes.first()
+ val intent = if (!isOnTv()) {
+ val mimeTypes = if (!filter.isNullOrEmpty()) {
+ val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
+ val mime = MimeTypeMap.getSingleton()
+ extensionRegex.findAll(filter).map {
+ it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
+ }.toSet()
+ } else emptySet()
+
+ Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ Log.v(TAG, "File mimyType filter: $mimeTypes")
+ if ("*/*" in mimeTypes) {
+ type = "*/*"
+ } else {
+ when (mimeTypes.size) {
+ 1 -> type = mimeTypes.first()
- in 2..Int.MAX_VALUE -> {
- type = "*/*"
- putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
- }
+ in 2..Int.MAX_VALUE -> {
+ type = "*/*"
+ putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
+ }
- else -> type = "*/*"
+ else -> type = "*/*"
+ }
}
}
- }.also {
- if (packageManager.resolveActivity(it, PackageManager.MATCH_DEFAULT_ONLY) == null) {
- Log.w(TAG, "Not found activity for ACTION_OPEN_DOCUMENT intent")
- it.action = Intent.ACTION_GET_CONTENT
- }
+ } else {
+ Intent(this@AmneziaActivity, TvFilePicker::class.java)
+ }
- try {
- startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
- onAny = {
- val uri = it?.data?.toString() ?: ""
- Log.v(TAG, "Open file: $uri")
- mainScope.launch {
- qtInitialized.await()
- QtAndroidController.onFileOpened(uri)
- }
+ try {
+ startActivityForResult(intent, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
+ onAny = {
+ if (isOnTv() && it?.hasExtra("activityNotFound") == true) {
+ showNoFileBrowserAlertDialog()
}
- ))
- } catch (_: ActivityNotFoundException) {
- Toast.makeText(this@AmneziaActivity, resources.getText(R.string.tvNoFileBrowser), Toast.LENGTH_LONG).show()
+ val uri = it?.data?.toString() ?: ""
+ Log.v(TAG, "Open file: $uri")
+ mainScope.launch {
+ qtInitialized.await()
+ QtAndroidController.onFileOpened(uri)
+ }
+ }
+ ))
+ } catch (_: ActivityNotFoundException) {
+ showNoFileBrowserAlertDialog()
+ mainScope.launch {
+ qtInitialized.await()
+ QtAndroidController.onFileOpened("")
}
}
}
}
+ private fun showNoFileBrowserAlertDialog() {
+ AlertDialog.Builder(this)
+ .setMessage(R.string.tvNoFileBrowser)
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ try {
+ startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
+ } catch (_: Throwable) {}
+ }
+ .show()
+ }
+
@Suppress("unused")
fun getFd(fileName: String): Int = try {
Log.v(TAG, "Get fd for $fileName")
diff --git a/client/android/src/org/amnezia/vpn/TvFilePicker.kt b/client/android/src/org/amnezia/vpn/TvFilePicker.kt
new file mode 100644
index 000000000..f3048509e
--- /dev/null
+++ b/client/android/src/org/amnezia/vpn/TvFilePicker.kt
@@ -0,0 +1,41 @@
+package org.amnezia.vpn
+
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.result.contract.ActivityResultContracts
+import org.amnezia.vpn.util.Log
+
+private const val TAG = "TvFilePicker"
+
+class TvFilePicker : ComponentActivity() {
+
+ private val fileChooseResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
+ setResult(RESULT_OK, Intent().apply { data = it })
+ finish()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Log.v(TAG, "onCreate")
+ getFile()
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ Log.v(TAG, "onNewIntent")
+ getFile()
+ }
+
+ private fun getFile() {
+ try {
+ Log.v(TAG, "getFile")
+ fileChooseResultLauncher.launch("*/*")
+ } catch (_: ActivityNotFoundException) {
+ Log.w(TAG, "Activity not found")
+ setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
+ finish()
+ }
+ }
+}