43f25e2e0d
Sieben Meldungen des Eigentuemers, beide Codebasen. Karte im Einzelbeleg: "Position laedt nicht" und "der Button funktioniert nicht" waren dieselbe Stelle - ohne aufloesbaren Ort malte das Panel einen beliebigen Ausschnitt und liess den Zentrieren-Knopf fuer immer disabled. Beide zeigen jetzt die Auskunft statt einer Karte und unterscheiden dabei "steht nicht im Beleg" von "findet niemand". Dazu ein echter Paritaetsmangel: die App schlug nur station_address nach, das Panel faellt seit jeher auf den Namen zurueck - von 16 Testbelegen tragen vier eine Anschrift. Der Suchbegriff darf Name und Anschrift NICHT zusammensetzen: gemessen findet Nominatim "Pascalstr. 8, 85057 Ingolstadt", mit dem Betreibernamen davor nichts. Jetzt zwei Kandidaten nacheinander (Anschrift, dann Name); fuer den Kartendienst dagegen beides zusammen, weil Apple und Google Betriebsnamen kennen. Tankstelle steht ueber die volle Kachelbreite, zweizeilig (Tankstelle und Strasse / Ort) und fuehrt beim Tippen in den Standard-Kartendienst - Apple Karten auf Apple-Geraeten, sonst Google Maps. Dieselbe Wahl jetzt auch fuer Fahrzeugstandort und Autohaus. SmartDeal-Ersparnis brach um, weil die dt-Spalte 150,00px breit war und ihr Inhalt 150,20px - zwei Zehntel Pixel, Zeile dadurch 44,8 statt 26,4px hoch. Der Regler "Fahrt beenden" schreibt nicht mehr beim Loslassen: derselbe Wert stellt den Schlaf-Timeout des Dongles, das soll bewusst ausgeloest werden. Speichern/Verwerfen erscheinen nur bei echter Aenderung. Share-Erweiterung: an der ausgelieferten .ipa gemessen trug sie 1.0, die App 2026.9.3.3 - iOS registriert eine Erweiterung mit abweichender Versionsnummer nicht, sie fehlt dann im Teilen-Blatt. ios-signieren.sh schreibt die Version jetzt in beide Ziele und liest die fertige .ipa gegen. Zwischenablage: dritter Weg ueber UIPasteboard.itemProviders, und ein fehlender nativer Teil wird als solcher gemeldet statt als "Kein Zugriff". Am Geraet gesetzt: 1003 Network Ping Timeout 0 -> 60 s, 1004 ACK Type TCP/IP -> AVL. Beide liegen bei flespi als pending bereit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.5 KiB
Swift
100 lines
4.5 KiB
Swift
//
|
|
// BelegZwischenablage.swift
|
|
// DataMetric360
|
|
//
|
|
// Holt ein PDF aus der System-Zwischenablage — der Weg, den die Web-API
|
|
// nicht anbietet.
|
|
//
|
|
// WARUM ES DAS BRAUCHT
|
|
// --------------------
|
|
// `navigator.clipboard.read()` gibt aus Datenschutzgründen nur Text, HTML
|
|
// und Bilder heraus; ein kopiertes PDF taucht dort NIE auf. Der Knopf „Aus
|
|
// Zwischenablage einfügen" endete deshalb auf dem Telefon zwangsläufig mit
|
|
// „In der Zwischenablage liegt kein PDF" (gemeldet vom Eigentümer,
|
|
// 02.09.2026). Das ist keine Lücke in unserem Code, sondern die Grenze der
|
|
// Schnittstelle.
|
|
//
|
|
// `UIPasteboard` kennt diese Grenze nicht: es gibt beliebige Typen heraus,
|
|
// auch `com.adobe.pdf`. Der Preis ist derselbe wie im Web — iOS zeigt beim
|
|
// Zugriff seine eigene Einfügen-Rückfrage, und das ist richtig so.
|
|
//
|
|
// DREI FORMEN, IN DENEN EIN PDF IN DER ZWISCHENABLAGE LIEGT
|
|
// ---------------------------------------------------------
|
|
// 1. Als Daten unter `com.adobe.pdf` — so legen es die meisten Apps ab.
|
|
// 2. Als Datei-URL — Mail kopiert einen Anhang oft nur als Verweis auf eine
|
|
// temporäre Kopie. Ohne den zweiten Weg bliebe genau der Fall übrig, für
|
|
// den der Knopf gedacht ist.
|
|
// 3. Als Zusage (`NSItemProvider`) — die Datei existiert dann noch gar nicht,
|
|
// die kopierende App liefert sie erst auf Anforderung. `data(forPasteboardType:)`
|
|
// gibt dabei nil zurück und `urls` bleibt leer; nur der Anbieter kennt sie.
|
|
// Das ist Apples eigener, empfohlener Weg und der einzige, der auch eine
|
|
// Datei aus einer Sandbox einer anderen App zuverlässig herausgibt — Weg 2
|
|
// scheitert dort am Lesezugriff. Er ist asynchron, deshalb steht er hier
|
|
// zuletzt: die beiden schnellen Wege sollen nicht auf ihn warten.
|
|
//
|
|
// DIESE DATEI IST VERSIONIERT, ios/ NICHT.
|
|
// `npx cap add ios` erzeugt den nativen Ordner neu und würde alles darin
|
|
// verlieren. scripts/ios-teilen-einrichten.mjs kopiert sie bei jedem Bau
|
|
// wieder hinein und hängt sie an die Sources-Phase des App-Ziels.
|
|
//
|
|
|
|
import Capacitor
|
|
import Foundation
|
|
import UIKit
|
|
import UniformTypeIdentifiers
|
|
|
|
@objc(BelegZwischenablagePlugin)
|
|
public class BelegZwischenablagePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
public let identifier = "BelegZwischenablagePlugin"
|
|
public let jsName = "BelegZwischenablage"
|
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
CAPPluginMethod(name: "pdfHolen", returnType: CAPPluginReturnPromise)
|
|
]
|
|
|
|
/// Liefert `{ base64, name }`, wenn ein PDF in der Zwischenablage liegt —
|
|
/// sonst ein leeres Ergebnis. Bewusst kein Fehler: „nichts kopiert" ist
|
|
/// kein Fehlschlag, sondern eine Antwort, und die Oberfläche sagt dazu
|
|
/// ohnehin das Passende (zwischenablage.ts).
|
|
@objc func pdfHolen(_ call: CAPPluginCall) {
|
|
let ablage = UIPasteboard.general
|
|
|
|
if let daten = ablage.data(forPasteboardType: UTType.pdf.identifier) {
|
|
return call.resolve(["base64": daten.base64EncodedString(), "name": "beleg.pdf"])
|
|
}
|
|
|
|
// Mail legt den Anhang oft nur als Verweis ab.
|
|
for url in ablage.urls ?? [] where url.pathExtension.lowercased() == "pdf" {
|
|
// Sicherheitsbereich anfordern: die URL zeigt auf eine Datei
|
|
// außerhalb des eigenen Sandkastens, sonst schlägt das Lesen fehl.
|
|
let erlaubt = url.startAccessingSecurityScopedResource()
|
|
defer { if erlaubt { url.stopAccessingSecurityScopedResource() } }
|
|
if let daten = try? Data(contentsOf: url) {
|
|
return call.resolve([
|
|
"base64": daten.base64EncodedString(),
|
|
"name": url.lastPathComponent,
|
|
])
|
|
}
|
|
}
|
|
|
|
// Dritter Weg: die Datei liegt nur als Zusage vor.
|
|
let anbieter = ablage.itemProviders.first {
|
|
$0.hasItemConformingToTypeIdentifier(UTType.pdf.identifier)
|
|
}
|
|
guard let anbieter else {
|
|
return call.resolve([:])
|
|
}
|
|
anbieter.loadDataRepresentation(forTypeIdentifier: UTType.pdf.identifier) { daten, _ in
|
|
guard let daten, !daten.isEmpty else {
|
|
// Kein Fehler: die Oberfläche sagt zu einem leeren Ergebnis
|
|
// ohnehin das Passende (zwischenablage.ts).
|
|
return call.resolve([:])
|
|
}
|
|
let name = anbieter.suggestedName.map { $0.hasSuffix(".pdf") ? $0 : $0 + ".pdf" }
|
|
call.resolve([
|
|
"base64": daten.base64EncodedString(),
|
|
"name": name ?? "beleg.pdf",
|
|
])
|
|
}
|
|
}
|
|
}
|