[+] Drop images

This commit is contained in:
2025-10-26 23:37:54 +08:00
parent 2809dfa040
commit b91de1de56
2 changed files with 105 additions and 47 deletions
+5
View File
@@ -26,6 +26,11 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
</application>
@@ -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<Uri?>(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<Picture?>(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<UploadWorker>()
.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<UploadWorker>()
.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 = {})
}
}