fix: legacy picker for real photo GPS on Android 13+, watermark shows place + capture datetime
This commit is contained in:
@@ -238,6 +238,14 @@ export default function App() {
|
|||||||
allowsEditing: false,
|
allowsEditing: false,
|
||||||
quality: 1,
|
quality: 1,
|
||||||
exif: true,
|
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) {
|
if (!result.canceled && result.assets && result.assets.length > 0) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import { Lock } from 'lucide-react-native';
|
|||||||
import { Recipe, GPSInfo, FrameId } from '../types';
|
import { Recipe, GPSInfo, FrameId } from '../types';
|
||||||
import { getSkiaColorMatrix, applyExposureGain } from '../utils/colorUtils';
|
import { getSkiaColorMatrix, applyExposureGain } from '../utils/colorUtils';
|
||||||
import { TONE_SKSL, getToneUniforms } from '../utils/toneShader';
|
import { TONE_SKSL, getToneUniforms } from '../utils/toneShader';
|
||||||
import { formatCoordinate } from '../utils/locationUtils';
|
|
||||||
|
|
||||||
interface ViewfinderProps {
|
interface ViewfinderProps {
|
||||||
mode: 'camera' | 'library';
|
mode: 'camera' | 'library';
|
||||||
@@ -481,19 +480,10 @@ export default function Viewfinder({
|
|||||||
const renderGPSWatermark = () => {
|
const renderGPSWatermark = () => {
|
||||||
if (!useGeotag || !gpsInfo || !customFont) return null;
|
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 locationName = gpsInfo.locality || 'STREET VIEW';
|
||||||
const timestampStr = new Date(gpsInfo.timestamp).toLocaleDateString('vi-VN');
|
const d = new Date(gpsInfo.timestamp);
|
||||||
const sourceTag =
|
const pad = (n: number) => String(n).padStart(2, '0');
|
||||||
gpsInfo.source === 'exif'
|
const timestampStr = `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||||
? 'EXIF'
|
|
||||||
: gpsInfo.source === 'mediaLibrary'
|
|
||||||
? 'MEDIA LIB'
|
|
||||||
: gpsInfo.source === 'device'
|
|
||||||
? 'DEVICE GPS'
|
|
||||||
: null;
|
|
||||||
const captionSuffix = sourceTag ? ` | ${sourceTag}` : ' | SS 1/125 f/2.8';
|
|
||||||
|
|
||||||
// Baseline above the bottom chrome (shutter rail) so the watermark stays
|
// Baseline above the bottom chrome (shutter rail) so the watermark stays
|
||||||
// readable; polaroid frames push it further up onto the white area edge.
|
// readable; polaroid frames push it further up onto the white area edge.
|
||||||
@@ -502,9 +492,8 @@ export default function Viewfinder({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Group>
|
<Group>
|
||||||
<SkiaText x={xOffset} y={yOffset - 32} text={`📍 ${locationName}`} font={customFont} color="#f59e0b" />
|
<SkiaText x={xOffset} y={yOffset - 16} text={`📍 ${locationName}`} font={customFont} color="#f59e0b" />
|
||||||
<SkiaText x={xOffset} y={yOffset - 16} text={`${latStr}, ${lonStr}`} font={customFont} color="#f59e0b" />
|
<SkiaText x={xOffset} y={yOffset} text={timestampStr} font={customFont} color="#f59e0b" />
|
||||||
<SkiaText x={xOffset} y={yOffset} text={`${timestampStr}${captionSuffix}`} font={customFont} color="#f59e0b" />
|
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { Recipe, GPSInfo, FrameId } from '../types';
|
|||||||
import { getSkiaColorMatrix, applyExposureGain } from './colorUtils';
|
import { getSkiaColorMatrix, applyExposureGain } from './colorUtils';
|
||||||
import { TONE_SKSL, getToneUniforms, toneIsActive } from './toneShader';
|
import { TONE_SKSL, getToneUniforms, toneIsActive } from './toneShader';
|
||||||
import { drawFrameOnCanvas } from './frameUtils';
|
import { drawFrameOnCanvas } from './frameUtils';
|
||||||
import { formatCoordinate } from './locationUtils';
|
|
||||||
import { patchJpegDpi } from './jpegDpi';
|
import { patchJpegDpi } from './jpegDpi';
|
||||||
|
|
||||||
export interface ExportResult {
|
export interface ExportResult {
|
||||||
@@ -252,26 +251,16 @@ export async function processAndExportPhoto(
|
|||||||
const textPaint = Skia.Paint();
|
const textPaint = Skia.Paint();
|
||||||
textPaint.setColor(Skia.Color('#f59e0b')); // Amber 500
|
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 locationName = gpsInfo.locality || 'STREET VIEW';
|
||||||
const timestampStr = new Date(gpsInfo.timestamp).toLocaleDateString('vi-VN');
|
const d = new Date(gpsInfo.timestamp);
|
||||||
const sourceTag =
|
const pad = (n: number) => String(n).padStart(2, '0');
|
||||||
gpsInfo.source === 'exif'
|
const timestampStr = `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||||
? 'EXIF'
|
|
||||||
: gpsInfo.source === 'mediaLibrary'
|
|
||||||
? 'MEDIA LIB'
|
|
||||||
: gpsInfo.source === 'device'
|
|
||||||
? 'DEVICE GPS'
|
|
||||||
: null;
|
|
||||||
const captionSuffix = sourceTag ? ` | ${sourceTag}` : ' | SS 1/125 f/2.8';
|
|
||||||
|
|
||||||
const yOffset = height - (frameId === 'polaroid' ? height * 0.15 : fontSize * 1.5);
|
const yOffset = height - (frameId === 'polaroid' ? height * 0.15 : fontSize * 1.5);
|
||||||
const xOffset = frameId === 'polaroid' ? width * 0.08 : fontSize * 1.2;
|
const xOffset = frameId === 'polaroid' ? width * 0.08 : fontSize * 1.2;
|
||||||
|
|
||||||
canvas.drawText(`📍 ${locationName}`, xOffset, yOffset - fontSize * 2.2, textPaint, font);
|
canvas.drawText(`📍 ${locationName}`, xOffset, yOffset - fontSize * 1.4, textPaint, font);
|
||||||
canvas.drawText(`${latStr}, ${lonStr}`, xOffset, yOffset - fontSize * 1.1, textPaint, font);
|
canvas.drawText(timestampStr, xOffset, yOffset, textPaint, font);
|
||||||
canvas.drawText(`${timestampStr}${captionSuffix}`, xOffset, yOffset, textPaint, font);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user