mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-21 16:32:39 +00:00
implement new file pickers
This commit is contained in:
4 files changed
+313
-1
No files matched your search
@@ -9,13 +9,28 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import android.provider.OpenableColumns
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import com.customRobTop.contracts.ImportFileActivityContract
|
||||||
|
import com.customRobTop.contracts.ShareFileActivityContract
|
||||||
import com.geode.launcher.R
|
import com.geode.launcher.R
|
||||||
|
import com.geode.launcher.utils.DownloadUtils
|
||||||
|
import com.geode.launcher.utils.LaunchUtils
|
||||||
|
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
|
||||||
import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.closeIMEKeyboard
|
import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.closeIMEKeyboard
|
||||||
import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.openIMEKeyboard
|
import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.openIMEKeyboard
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.OutputStream
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -24,6 +39,7 @@ object BaseRobTopActivity {
|
|||||||
const val KEYBOARD_INPUT_MULTILINE = 1
|
const val KEYBOARD_INPUT_MULTILINE = 1
|
||||||
const val KEYBOARD_INPUT_NUMERIC = 4
|
const val KEYBOARD_INPUT_NUMERIC = 4
|
||||||
const val KEYBOARD_INPUT_PASSWORD = 2
|
const val KEYBOARD_INPUT_PASSWORD = 2
|
||||||
|
const val LEVEL_EXTENSION: String = ".gdlvl"
|
||||||
|
|
||||||
private var isLoaded = false
|
private var isLoaded = false
|
||||||
var blockBackButton = false
|
var blockBackButton = false
|
||||||
@@ -35,8 +51,57 @@ object BaseRobTopActivity {
|
|||||||
lateinit var me: WeakReference<Activity>
|
lateinit var me: WeakReference<Activity>
|
||||||
private var shouldResumeSound = true
|
private var shouldResumeSound = true
|
||||||
|
|
||||||
fun setCurrentActivity(currentActivity: Activity) {
|
private var pendingExportPaths = emptyList<String>()
|
||||||
|
|
||||||
|
private lateinit var importFileActivityContract: ActivityResultLauncher<Unit>
|
||||||
|
private lateinit var shareFileActivityContract: ActivityResultLauncher<String>
|
||||||
|
private lateinit var shareFolderActivityContract: ActivityResultLauncher<Uri?>
|
||||||
|
|
||||||
|
fun setCurrentActivity(currentActivity: AppCompatActivity) {
|
||||||
me = WeakReference(currentActivity)
|
me = WeakReference(currentActivity)
|
||||||
|
|
||||||
|
importFileActivityContract = currentActivity.registerForActivityResult(ImportFileActivityContract()) { uris ->
|
||||||
|
val filePaths = copyPickedFiles(uris)
|
||||||
|
|
||||||
|
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||||
|
JniToCpp.filesPicked(filePaths.toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shareFileActivityContract = currentActivity.registerForActivityResult(ShareFileActivityContract()) { uri ->
|
||||||
|
val currentlyPending = pendingExportPaths
|
||||||
|
pendingExportPaths = emptyList()
|
||||||
|
|
||||||
|
val exportPath = currentlyPending.firstOrNull()
|
||||||
|
if (exportPath == null || uri == null) {
|
||||||
|
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||||
|
JniToCpp.fileShareFinished(false)
|
||||||
|
}
|
||||||
|
return@registerForActivityResult
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = saveToDocument(exportPath, uri)
|
||||||
|
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||||
|
JniToCpp.fileShareFinished(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shareFolderActivityContract = currentActivity.registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
|
||||||
|
val currentlyPending = pendingExportPaths
|
||||||
|
pendingExportPaths = emptyList()
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||||
|
JniToCpp.fileShareFinished(false)
|
||||||
|
}
|
||||||
|
return@registerForActivityResult
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = saveToFolder(currentlyPending, uri) > 0
|
||||||
|
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||||
|
JniToCpp.fileShareFinished(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("HardwareIds")
|
@SuppressLint("HardwareIds")
|
||||||
@@ -289,4 +354,159 @@ object BaseRobTopActivity {
|
|||||||
fun setKeyboardInputFlags(flags: Int) {
|
fun setKeyboardInputFlags(flags: Int) {
|
||||||
curKeyboardInputFlags = flags
|
curKeyboardInputFlags = flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun pickImportFiles() {
|
||||||
|
try {
|
||||||
|
importFileActivityContract.launch(Unit)
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
me.get()?.apply {
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
R.string.no_activity_found,
|
||||||
|
Toast.LENGTH_SHORT,
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyPickedFiles(uris: List<Uri>): List<String> {
|
||||||
|
val me = me.get() ?: return emptyList()
|
||||||
|
|
||||||
|
val importsDir = File(LaunchUtils.getSaveDirectory(me), "imports")
|
||||||
|
importsDir.mkdirs()
|
||||||
|
|
||||||
|
return uris.mapIndexedNotNull { index, uri ->
|
||||||
|
val displayName = displayName(uri)
|
||||||
|
?: "import_$index$LEVEL_EXTENSION"
|
||||||
|
if (!displayName.endsWith(LEVEL_EXTENSION)) {
|
||||||
|
return@mapIndexedNotNull null
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputFile = File(importsDir, displayName)
|
||||||
|
var counter = 2
|
||||||
|
while (outputFile.exists()) {
|
||||||
|
outputFile = File(importsDir, "${counter}_$displayName")
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
me.contentResolver.openInputStream(uri)?.use { input ->
|
||||||
|
outputFile.outputStream().use { output ->
|
||||||
|
DownloadUtils.copyFile(input, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFile.absolutePath
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
outputFile.delete()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayName(uri: Uri): String? {
|
||||||
|
me.get()?.contentResolver?.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursorQuery ->
|
||||||
|
if (cursorQuery.moveToFirst()) {
|
||||||
|
val displayName = cursorQuery.getString(0)
|
||||||
|
if (displayName.isNotEmpty()) {
|
||||||
|
return File(displayName).name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun shareFiles(files: Array<String>) {
|
||||||
|
pendingExportPaths = files.toList()
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (files.size == 1) {
|
||||||
|
shareFileActivityContract.launch(files.first())
|
||||||
|
return
|
||||||
|
} else if (files.size > 1) {
|
||||||
|
shareFolderActivityContract.launch(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
me.get()?.apply {
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
R.string.no_activity_found,
|
||||||
|
Toast.LENGTH_SHORT,
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||||
|
JniToCpp.fileShareFinished(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveToFolder(exportPaths: List<String>, treeUri: Uri): Int {
|
||||||
|
val uri = DocumentsContract.buildDocumentUriUsingTree(treeUri, DocumentsContract.getTreeDocumentId(treeUri))
|
||||||
|
|
||||||
|
val me = me.get() ?: return 0
|
||||||
|
|
||||||
|
return exportPaths.count { path ->
|
||||||
|
val filename = File(path).name
|
||||||
|
val documentUri = DocumentsContract.createDocument(me.contentResolver, uri, "application/octet-stream", filename)
|
||||||
|
?: return@count false
|
||||||
|
|
||||||
|
saveToDocument(path, documentUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveToDocument(exportPath: String, uri: Uri): Boolean {
|
||||||
|
try {
|
||||||
|
FileInputStream(exportPath).use { inputStream ->
|
||||||
|
openOutput(uri).use { outputStream ->
|
||||||
|
if (outputStream != null) {
|
||||||
|
DownloadUtils.copyFile(inputStream, outputStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureLevelExtension(uri)
|
||||||
|
return true
|
||||||
|
} catch (_: IOException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openOutput(uri: Uri): OutputStream? {
|
||||||
|
val me = me.get() ?: return null
|
||||||
|
return try {
|
||||||
|
me.contentResolver.openOutputStream(uri, "wt")
|
||||||
|
} catch (_: IOException) {
|
||||||
|
me.contentResolver.openOutputStream(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ensureLevelExtension(uri: Uri) {
|
||||||
|
val displayName = displayName(uri)
|
||||||
|
if (displayName == null || displayName.endsWith(LEVEL_EXTENSION)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val me = me.get() ?: return
|
||||||
|
|
||||||
|
val extensionIdx = displayName.lastIndexOf(".gdlvl.")
|
||||||
|
val substring = if (extensionIdx < 0) {
|
||||||
|
displayName + LEVEL_EXTENSION
|
||||||
|
} else {
|
||||||
|
displayName.substring(0, extensionIdx + 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DocumentsContract.renameDocument(me.contentResolver, uri, substring)
|
||||||
|
} catch (_: IOException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -55,4 +55,16 @@ object JniToCpp {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun videoAdShowed()
|
external fun videoAdShowed()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
external fun filesPicked(files: Array<String>)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
external fun fileShareFinished(success: Boolean)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
external fun textInputCaretToEnd()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
external fun textInputDone()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.customRobTop.contracts
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
|
||||||
|
class ImportFileActivityContract : ActivityResultContract<Unit, List<Uri>>() {
|
||||||
|
override fun createIntent(
|
||||||
|
context: Context,
|
||||||
|
input: Unit
|
||||||
|
): Intent {
|
||||||
|
return Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "*/*"
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(
|
||||||
|
resultCode: Int,
|
||||||
|
intent: Intent?
|
||||||
|
): List<Uri> {
|
||||||
|
if (resultCode != Activity.RESULT_OK || intent == null) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val clipData = intent.clipData
|
||||||
|
return if (clipData != null) {
|
||||||
|
List(clipData.itemCount) { i -> clipData.getItemAt(i).uri }
|
||||||
|
} else {
|
||||||
|
val intentData = intent.data
|
||||||
|
if (intentData != null)
|
||||||
|
listOf(intentData)
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.customRobTop.contracts
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ShareFileActivityContract : ActivityResultContract<String, Uri?>() {
|
||||||
|
override fun createIntent(
|
||||||
|
context: Context,
|
||||||
|
input: String
|
||||||
|
): Intent {
|
||||||
|
return Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "application/octet-stream"
|
||||||
|
putExtra(Intent.EXTRA_TITLE, File(input).name)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
putExtra(DocumentsContract.EXTRA_INITIAL_URI,
|
||||||
|
DocumentsContract.buildDocumentUri("com.android.externalstorage.documents", "primary:Download")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(
|
||||||
|
resultCode: Int,
|
||||||
|
intent: Intent?
|
||||||
|
): Uri? {
|
||||||
|
if (resultCode != Activity.RESULT_OK || intent == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return intent.data
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user