From 43a73c541b3fa03ee707dec3103e0deef60a78e3 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Tue, 8 Sep 2026 11:28:02 +0700 Subject: [PATCH] fix: legacy picker for real photo GPS on Android 13+, watermark shows place + capture datetime --- App.tsx | 8 ++++++++ src/components/Viewfinder.tsx | 21 +++++---------------- src/utils/exportEngine.ts | 21 +++++---------------- 3 files changed, 18 insertions(+), 32 deletions(-) diff --git a/App.tsx b/App.tsx index a6c4de9..b8b2a97 100644 --- a/App.tsx +++ b/App.tsx @@ -238,6 +238,14 @@ export default function App() { allowsEditing: false, quality: 1, exif: true, + // Android 13+ photo picker always returns a redacted cache copy with + // assetId=null — EXIF GPS stripped, MediaStore id lost → photo GPS is + // unreachable and we'd stamp the device's location instead. The legacy + // picker (ACTION_GET_CONTENT → DocumentsUI) hands back a + // com.android.providers.media.documents uri, so the MediaStore asset id + // survives AND (with ACCESS_MEDIA_LOCATION granted) the file is not + // redacted → tier-1 EXIF / tier-2 MediaStore both work. + legacy: Platform.OS === 'android' && useGeotag, }); if (!result.canceled && result.assets && result.assets.length > 0) { diff --git a/src/components/Viewfinder.tsx b/src/components/Viewfinder.tsx index 2c06190..08e8e7e 100644 --- a/src/components/Viewfinder.tsx +++ b/src/components/Viewfinder.tsx @@ -24,7 +24,6 @@ import { Lock } from 'lucide-react-native'; import { Recipe, GPSInfo, FrameId } from '../types'; import { getSkiaColorMatrix, applyExposureGain } from '../utils/colorUtils'; import { TONE_SKSL, getToneUniforms } from '../utils/toneShader'; -import { formatCoordinate } from '../utils/locationUtils'; interface ViewfinderProps { mode: 'camera' | 'library'; @@ -481,19 +480,10 @@ export default function Viewfinder({ const renderGPSWatermark = () => { if (!useGeotag || !gpsInfo || !customFont) return null; - const latStr = formatCoordinate(gpsInfo.latitude, 'lat'); - const lonStr = formatCoordinate(gpsInfo.longitude, 'lon'); const locationName = gpsInfo.locality || 'STREET VIEW'; - const timestampStr = new Date(gpsInfo.timestamp).toLocaleDateString('vi-VN'); - const sourceTag = - gpsInfo.source === 'exif' - ? 'EXIF' - : gpsInfo.source === 'mediaLibrary' - ? 'MEDIA LIB' - : gpsInfo.source === 'device' - ? 'DEVICE GPS' - : null; - const captionSuffix = sourceTag ? ` | ${sourceTag}` : ' | SS 1/125 f/2.8'; + const d = new Date(gpsInfo.timestamp); + const pad = (n: number) => String(n).padStart(2, '0'); + const timestampStr = `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`; // Baseline above the bottom chrome (shutter rail) so the watermark stays // readable; polaroid frames push it further up onto the white area edge. @@ -502,9 +492,8 @@ export default function Viewfinder({ return ( - - - + + ); }; diff --git a/src/utils/exportEngine.ts b/src/utils/exportEngine.ts index 6e524dd..4211be0 100644 --- a/src/utils/exportEngine.ts +++ b/src/utils/exportEngine.ts @@ -7,7 +7,6 @@ import { Recipe, GPSInfo, FrameId } from '../types'; import { getSkiaColorMatrix, applyExposureGain } from './colorUtils'; import { TONE_SKSL, getToneUniforms, toneIsActive } from './toneShader'; import { drawFrameOnCanvas } from './frameUtils'; -import { formatCoordinate } from './locationUtils'; import { patchJpegDpi } from './jpegDpi'; export interface ExportResult { @@ -252,26 +251,16 @@ export async function processAndExportPhoto( const textPaint = Skia.Paint(); textPaint.setColor(Skia.Color('#f59e0b')); // Amber 500 - const latStr = formatCoordinate(gpsInfo.latitude, 'lat'); - const lonStr = formatCoordinate(gpsInfo.longitude, 'lon'); const locationName = gpsInfo.locality || 'STREET VIEW'; - const timestampStr = new Date(gpsInfo.timestamp).toLocaleDateString('vi-VN'); - const sourceTag = - gpsInfo.source === 'exif' - ? 'EXIF' - : gpsInfo.source === 'mediaLibrary' - ? 'MEDIA LIB' - : gpsInfo.source === 'device' - ? 'DEVICE GPS' - : null; - const captionSuffix = sourceTag ? ` | ${sourceTag}` : ' | SS 1/125 f/2.8'; + const d = new Date(gpsInfo.timestamp); + const pad = (n: number) => String(n).padStart(2, '0'); + const timestampStr = `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`; const yOffset = height - (frameId === 'polaroid' ? height * 0.15 : fontSize * 1.5); const xOffset = frameId === 'polaroid' ? width * 0.08 : fontSize * 1.2; - canvas.drawText(`📍 ${locationName}`, xOffset, yOffset - fontSize * 2.2, textPaint, font); - canvas.drawText(`${latStr}, ${lonStr}`, xOffset, yOffset - fontSize * 1.1, textPaint, font); - canvas.drawText(`${timestampStr}${captionSuffix}`, xOffset, yOffset, textPaint, font); + canvas.drawText(`📍 ${locationName}`, xOffset, yOffset - fontSize * 1.4, textPaint, font); + canvas.drawText(timestampStr, xOffset, yOffset, textPaint, font); } }