[+] Drop images
This commit is contained in:
@@ -26,6 +26,11 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</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>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package aza.instant
|
package aza.instant
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.Picture
|
import android.graphics.Picture
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.OpenableColumns
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
@@ -51,20 +54,38 @@ import java.io.File
|
|||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
private var sharedImageUri by mutableStateOf<Uri?>(null)
|
||||||
|
private var isUriHandled = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
handleIntent(intent)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
ProjectInstantTheme {
|
ProjectInstantTheme {
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) {
|
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
|
@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 feedbackText by remember { mutableStateOf("Click a button to see the output here.") }
|
||||||
var picture by remember { mutableStateOf<Picture?>(null) }
|
var picture by remember { mutableStateOf<Picture?>(null) }
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
@@ -81,10 +102,66 @@ fun FileListerScreen(modifier: Modifier = Modifier) {
|
|||||||
.toTypedArray().ifEmpty { null }?.let { askPerm.launch(it) }
|
.toTypedArray().ifEmpty { null }?.let { askPerm.launch(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
val renderAndUpload = { imagePath: String, originalFileName: String ->
|
||||||
modifier = modifier.fillMaxSize(),
|
coroutineScope.launch {
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
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) {
|
Column(modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
picture?.let { p ->
|
picture?.let { p ->
|
||||||
Canvas(modifier = Modifier.fillMaxWidth().weight(1f).drawWithCache {
|
Canvas(modifier = Modifier.fillMaxWidth().weight(1f).drawWithCache {
|
||||||
@@ -102,61 +179,37 @@ fun FileListerScreen(modifier: Modifier = Modifier) {
|
|||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
feedbackText = "Rendering..."
|
|
||||||
val imageDir = File("/storage/emulated/0/DCIM/CA_IMAGES/")
|
val imageDir = File("/storage/emulated/0/DCIM/CA_IMAGES/")
|
||||||
val outDir = File("/storage/emulated/0/DCIM/CA_IMAGES_OUT/")
|
|
||||||
val latestImg = imageDir.listFiles()
|
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() }
|
?.maxByOrNull { it.lastModified() }
|
||||||
if (latestImg == null) {
|
if (latestImg == null) {
|
||||||
feedbackText = "No images found in CA_IMAGES"
|
feedbackText = "No images found in CA_IMAGES"
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
try {
|
renderAndUpload(latestImg.absolutePath, latestImg.name)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}) { Text(text = "Render Latest") }
|
}) { 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 {
|
private fun listFilesFromDcim(): String {
|
||||||
val d = File("/storage/emulated/0/DCIM/CA_IMAGES/")
|
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}"
|
if (!d.exists() || !d.isDirectory) return "Directory not found or is not a directory: ${d.absolutePath}"
|
||||||
return d.listFiles()?.let {
|
return d.listFiles()?.let { files ->
|
||||||
it.ifEmpty { null }?.sortedBy { it.lastModified() }?.joinToString("\n") { it.name }
|
files.ifEmpty { null }?.sortedBy { it.lastModified() }?.joinToString("\n") { it.name }
|
||||||
?: "No files found in ${d.absolutePath}"
|
?: "No files found in ${d.absolutePath}"
|
||||||
} ?: "Failed to list files in ${d.absolutePath}. listFiles() returned null."
|
} ?: "Failed to list files in ${d.absolutePath}. listFiles() returned null."
|
||||||
}
|
}
|
||||||
@@ -165,6 +218,6 @@ private fun listFilesFromDcim(): String {
|
|||||||
@Composable
|
@Composable
|
||||||
fun FileListerScreenPreview() {
|
fun FileListerScreenPreview() {
|
||||||
ProjectInstantTheme {
|
ProjectInstantTheme {
|
||||||
FileListerScreen()
|
FileListerScreen(sharedImageUri = null, onSharedImageHandled = {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user