From b91de1de56e81a92b8144571bb069e92aaeed1cb Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Sun, 26 Oct 2025 23:37:54 +0800 Subject: [PATCH] [+] Drop images --- android/app/src/main/AndroidManifest.xml | 5 + .../src/main/java/aza/instant/MainActivity.kt | 147 ++++++++++++------ 2 files changed, 105 insertions(+), 47 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 91d4909..e6f5fcb 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -26,6 +26,11 @@ + + + + + diff --git a/android/app/src/main/java/aza/instant/MainActivity.kt b/android/app/src/main/java/aza/instant/MainActivity.kt index 3385b83..d70faef 100644 --- a/android/app/src/main/java/aza/instant/MainActivity.kt +++ b/android/app/src/main/java/aza/instant/MainActivity.kt @@ -1,12 +1,15 @@ package aza.instant import android.Manifest +import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.graphics.Bitmap import android.graphics.Picture import android.media.MediaScannerConnection +import android.net.Uri +import android.os.Build import android.os.Bundle +import android.provider.OpenableColumns import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult @@ -51,20 +54,38 @@ import java.io.File import java.io.InputStreamReader class MainActivity : ComponentActivity() { + private var sharedImageUri by mutableStateOf(null) + private var isUriHandled = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + handleIntent(intent) + setContent { ProjectInstantTheme { Scaffold(modifier = Modifier.fillMaxSize()) { - FileListerScreen(modifier = Modifier.padding(it)) + FileListerScreen(modifier = Modifier.padding(it), sharedImageUri = if (isUriHandled) null else sharedImageUri, onSharedImageHandled = { isUriHandled = true }) } } } } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + setIntent(intent) + isUriHandled = false + handleIntent(intent) + } + + private fun handleIntent(intent: Intent?) { + if (intent?.action == Intent.ACTION_SEND && intent.type?.startsWith("image/") == true) { + sharedImageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java) + } + } } @Composable -fun FileListerScreen(modifier: Modifier = Modifier) { +fun FileListerScreen(modifier: Modifier = Modifier, sharedImageUri: Uri?, onSharedImageHandled: () -> Unit) { var feedbackText by remember { mutableStateOf("Click a button to see the output here.") } var picture by remember { mutableStateOf(null) } val scrollState = rememberScrollState() @@ -81,10 +102,66 @@ fun FileListerScreen(modifier: Modifier = Modifier) { .toTypedArray().ifEmpty { null }?.let { askPerm.launch(it) } } - Column( - modifier = modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally - ) { + val renderAndUpload = { imagePath: String, originalFileName: String -> + coroutineScope.launch { + try { + feedbackText = "Rendering..." + val outDir = File("/storage/emulated/0/DCIM/CA_IMAGES_OUT/") + val templateStream = context.resources.openRawResource(R.raw.postcard4) + val template = BufferedReader(InputStreamReader(templateStream)).readText() + val (svg, exif) = genSvg(template, imagePath) + val (pic, bytes) = renderSvgBytes(context, svg, exif) ?: return@launch + picture = pic + + // Save picture to file + outDir.mkdirs() + val out = File(outDir, "${File(originalFileName).nameWithoutExtension}-framed.jpg") + .apply { writeBytes(bytes) } + + MediaScannerConnection.scanFile(context, arrayOf(out.absolutePath), null, null) + + feedbackText = "Rendered ${originalFileName}, now uploading..." + + val uploadWorkRequest = OneTimeWorkRequestBuilder() + .setInputData(workDataOf( + "originalPhotoPath" to imagePath, + "editedPhotoPath" to out.absolutePath, + "urlName" to exif.urlName + )) + .build() + WorkManager.getInstance(context).enqueue(uploadWorkRequest) + + // Launch Canon app + context.startActivity(Intent().apply { + setClassName("jp.co.canon.ic.photolayout", "jp.co.canon.ic.photolayout.MainActivity") + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + }) + } catch (e: Exception) { + feedbackText = "Error during operation: ${e.message}" + Log.e("MainActivity", "Error during render or upload", e) + } + } + } + + LaunchedEffect(sharedImageUri) { + if (sharedImageUri == null) return@LaunchedEffect + + feedbackText = "Handling shared image..." + val fileName = getFileName(context, sharedImageUri) ?: "shared_image_${System.currentTimeMillis()}.jpg" + val tempFile = File(context.cacheDir, fileName) + try { + context.contentResolver.openInputStream(sharedImageUri)?.use { input -> + tempFile.outputStream().use { output -> input.copyTo(output) } + } + renderAndUpload(tempFile.absolutePath, fileName) + onSharedImageHandled() + } catch (e: Exception) { + feedbackText = "Failed to handle shared image: ${e.message}" + Log.e("MainActivity", "Error handling shared image", e) + } + } + + Column(modifier = modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { Column(modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally) { picture?.let { p -> Canvas(modifier = Modifier.fillMaxWidth().weight(1f).drawWithCache { @@ -102,61 +179,37 @@ fun FileListerScreen(modifier: Modifier = Modifier) { Spacer(modifier = Modifier.width(8.dp)) Button(onClick = { coroutineScope.launch { - feedbackText = "Rendering..." val imageDir = File("/storage/emulated/0/DCIM/CA_IMAGES/") - val outDir = File("/storage/emulated/0/DCIM/CA_IMAGES_OUT/") val latestImg = imageDir.listFiles() - ?.filter { !it.name.contains("-framed") and !it.name.contains(".trashed") } + ?.filter { !it.name.contains("-framed") && !it.name.contains(".trashed") } ?.maxByOrNull { it.lastModified() } if (latestImg == null) { feedbackText = "No images found in CA_IMAGES" return@launch } - try { - val templateStream = context.resources.openRawResource(R.raw.postcard4) - val template = BufferedReader(InputStreamReader(templateStream)).readText() - val (svg, exif) = genSvg(template, latestImg.absolutePath) - val (pic, bytes) = renderSvgBytes(context, svg, exif) ?: return@launch - picture = pic - - // Save picture to file - outDir.mkdirs() - val out = File(outDir, "${latestImg.nameWithoutExtension}-framed.jpg") - .apply { writeBytes(bytes) } - - MediaScannerConnection.scanFile(context, arrayOf(out.absolutePath), null, null) - - feedbackText = "Rendered ${latestImg.name}, now uploading..." - - val uploadWorkRequest = OneTimeWorkRequestBuilder() - .setInputData(workDataOf( - "originalPhotoPath" to latestImg.absolutePath, - "editedPhotoPath" to out.absolutePath, - "urlName" to exif.urlName - )) - .build() - WorkManager.getInstance(context).enqueue(uploadWorkRequest) - - // Launch Canon app - context.startActivity(Intent().apply { - setClassName("jp.co.canon.ic.photolayout", "jp.co.canon.ic.photolayout.MainActivity") - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - }) - } catch (e: Exception) { - feedbackText = "Error during operation: ${e.message}" - Log.e("MainActivity", "Error during render or upload", e) - } + renderAndUpload(latestImg.absolutePath, latestImg.name) } }) { Text(text = "Render Latest") } } } } +private fun getFileName(context: Context, uri: Uri): String? { + context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> + if (cursor.moveToFirst()) { + val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + if (nameIndex != -1) return cursor.getString(nameIndex) + } + } + return uri.path?.substringAfterLast('/') +} + + private fun listFilesFromDcim(): String { val d = File("/storage/emulated/0/DCIM/CA_IMAGES/") if (!d.exists() || !d.isDirectory) return "Directory not found or is not a directory: ${d.absolutePath}" - return d.listFiles()?.let { - it.ifEmpty { null }?.sortedBy { it.lastModified() }?.joinToString("\n") { it.name } + return d.listFiles()?.let { files -> + files.ifEmpty { null }?.sortedBy { it.lastModified() }?.joinToString("\n") { it.name } ?: "No files found in ${d.absolutePath}" } ?: "Failed to list files in ${d.absolutePath}. listFiles() returned null." } @@ -165,6 +218,6 @@ private fun listFilesFromDcim(): String { @Composable fun FileListerScreenPreview() { ProjectInstantTheme { - FileListerScreen() + FileListerScreen(sharedImageUri = null, onSharedImageHandled = {}) } }