feat(exif): write the Xiaomi watermark blob (0x889e) so HyperOS Gallery can frame our captures
HyperOS Gallery reads EXIF 0x889e (a JSON blob) to build its watermark frame; without it a photo fails with "cannot recognize the parameters". CameraX/HAL never supplies that vendor tag, so a capture from this app lacked it while a stock-camera photo carried it. Read back the same props the stock camera derives the blob from (ro.product.device / ro.product.marketname / Build.MANUFACTURER) via the native module, synthesize the blob in exifWrite, and write it only when the source has none and the device is Xiaomi/Redmi/POCO - a source blob is never overwritten.
This commit is contained in:
+27
@@ -58,6 +58,17 @@ private class FramedResult(val bmp: Bitmap, val window: RectF?)
|
||||
// Font temp files are tiny and reused across exports — cache the Typefaces.
|
||||
private val typefaceCache = HashMap<String, Typeface>()
|
||||
|
||||
// android.os.SystemProperties is hidden API: reflection keeps this compiling
|
||||
// without stubs and degrades to null if a future SDK moves it.
|
||||
private fun systemProp(key: String): String? = try {
|
||||
val get = Class.forName("android.os.SystemProperties")
|
||||
.getMethod("get", String::class.java, String::class.java)
|
||||
(get.invoke(null, key, "") as String).ifEmpty { null }
|
||||
} catch (e: Throwable) {
|
||||
Log.w(TAG, "systemProp($key) unavailable: ${e.message}")
|
||||
null
|
||||
}
|
||||
|
||||
class RecipescamExportModule : Module() {
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("RecipesCamExport")
|
||||
@@ -104,6 +115,22 @@ class RecipescamExportModule : Module() {
|
||||
"pong"
|
||||
}
|
||||
|
||||
// Raw device identity the stock Xiaomi camera folds into EXIF 0x889e —
|
||||
// the watermark blob HyperOS Gallery reads before it will open its
|
||||
// watermark editor. CameraX/HAL never writes that tag, so Gallery reports
|
||||
// it cannot recognize the parameters on our captures. JS formats the JSON
|
||||
// (see exifWrite.watermarkBlob); native only reads the props.
|
||||
Function("deviceInfo") { ->
|
||||
mapOf(
|
||||
"device" to (systemProp("ro.product.device") ?: ""),
|
||||
"marketName" to (systemProp("ro.product.marketname")
|
||||
?: systemProp("ro.product.odm.marketname")
|
||||
?: systemProp("ro.product.vendor.marketname") ?: ""),
|
||||
"manufacturer" to (android.os.Build.MANUFACTURER ?: ""),
|
||||
"model" to (android.os.Build.MODEL ?: ""),
|
||||
)
|
||||
}
|
||||
|
||||
// P0 spike harness: copy a bundled drawable asset (e.g. "wallframe", the
|
||||
// 3117x4000 artwork — representative 12MP decode) into cacheDir and hand the
|
||||
// real file path back to JS, which then runs decodeEncodeAsync on it. This
|
||||
|
||||
@@ -91,6 +91,13 @@ export interface RecipescamExportModule {
|
||||
processPhotoAsync(srcPath: string, dstPath: string, matrix: number[], crop: CropRect | null, adjust?: PhotoAdjust): Promise<ColorTimings>;
|
||||
/** P1 alias: matrix + crop, no tone/cinema/enhance passes. */
|
||||
processColorAsync(srcPath: string, dstPath: string, matrix: number[], crop: CropRect | null, quality: number): Promise<ColorTimings>;
|
||||
/**
|
||||
* Raw device identity the stock Xiaomi camera folds into EXIF 0x889e (the
|
||||
* watermark blob HyperOS Gallery reads) but CameraX/HAL never supplies:
|
||||
* codename, marketing name, manufacturer, model. Empty strings when absent.
|
||||
*/
|
||||
deviceInfo(): { device: string; marketName: string; manufacturer: string; model: string };
|
||||
|
||||
/** P0 probe helpers — removed before ship. */
|
||||
ping(): Promise<string>;
|
||||
materializeBenchAsset(assetName: string): Promise<string>;
|
||||
|
||||
Reference in New Issue
Block a user