mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 03:45:20 +00:00
Harden PTT audio and the 1.7.1 release (#1423)
Centralize PTT and voice audio-session ownership, harden courier/bridge/outbox delivery and recovery, correct location and delivery-state races, add privacy/release metadata, and ship reproducible universal Arti slices with Release CI coverage. Validated by the full iOS suite, repeated audio/fragment/performance regressions, BitFoundation tests, strict lint and dead-code analysis, universal iOS Release builds, and iOS/macOS archives.
This commit is contained in:
@@ -28,8 +28,6 @@
|
||||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>1C8F.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -107,38 +107,46 @@ final class ShareViewController: UIViewController {
|
||||
|
||||
private func loadFirstURL(from providers: [NSItemProvider], completion: @escaping (URL?) -> Void) {
|
||||
let identifiers = [UTType.url.identifier, "public.url", "public.file-url"]
|
||||
let grp = DispatchGroup()
|
||||
var found: URL?
|
||||
|
||||
for p in providers where found == nil {
|
||||
for id in identifiers where p.hasItemConformingToTypeIdentifier(id) {
|
||||
grp.enter()
|
||||
p.loadItem(forTypeIdentifier: id, options: nil) { item, _ in
|
||||
defer { grp.leave() }
|
||||
if let u = item as? URL { found = u; return }
|
||||
if let s = item as? String, let u = URL(string: s) { found = u; return }
|
||||
if let d = item as? Data, let s = String(data: d, encoding: .utf8), let u = URL(string: s) { found = u; return }
|
||||
}
|
||||
break
|
||||
for provider in providers {
|
||||
guard let identifier = identifiers.first(where: { provider.hasItemConformingToTypeIdentifier($0) }) else {
|
||||
continue
|
||||
}
|
||||
provider.loadItem(forTypeIdentifier: identifier, options: nil) { item, _ in
|
||||
let result: URL?
|
||||
if let url = item as? URL {
|
||||
result = url
|
||||
} else if let string = item as? String {
|
||||
result = URL(string: string)
|
||||
} else if let data = item as? Data,
|
||||
let string = String(data: data, encoding: .utf8) {
|
||||
result = URL(string: string)
|
||||
} else {
|
||||
result = nil
|
||||
}
|
||||
DispatchQueue.main.async { completion(result) }
|
||||
}
|
||||
return
|
||||
}
|
||||
grp.notify(queue: .main) { completion(found) }
|
||||
DispatchQueue.main.async { completion(nil) }
|
||||
}
|
||||
|
||||
private func loadFirstPlainText(from providers: [NSItemProvider], completion: @escaping (String?) -> Void) {
|
||||
let id = UTType.plainText.identifier
|
||||
let grp = DispatchGroup()
|
||||
var text: String?
|
||||
for p in providers where p.hasItemConformingToTypeIdentifier(id) {
|
||||
grp.enter()
|
||||
p.loadItem(forTypeIdentifier: id, options: nil) { item, _ in
|
||||
defer { grp.leave() }
|
||||
if let s = item as? String { text = s }
|
||||
else if let d = item as? Data, let s = String(data: d, encoding: .utf8) { text = s }
|
||||
let identifier = UTType.plainText.identifier
|
||||
guard let provider = providers.first(where: { $0.hasItemConformingToTypeIdentifier(identifier) }) else {
|
||||
DispatchQueue.main.async { completion(nil) }
|
||||
return
|
||||
}
|
||||
provider.loadItem(forTypeIdentifier: identifier, options: nil) { item, _ in
|
||||
let result: String?
|
||||
if let string = item as? String {
|
||||
result = string
|
||||
} else if let data = item as? Data {
|
||||
result = String(data: data, encoding: .utf8)
|
||||
} else {
|
||||
result = nil
|
||||
}
|
||||
break
|
||||
DispatchQueue.main.async { completion(result) }
|
||||
}
|
||||
grp.notify(queue: .main) { completion(text) }
|
||||
}
|
||||
|
||||
// MARK: - Save + Finish
|
||||
@@ -170,10 +178,13 @@ final class ShareViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func finishWithMessage(_ msg: String) {
|
||||
statusLabel.text = msg
|
||||
// Complete shortly after showing status
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + TransportConfig.uiShareExtensionDismissDelaySeconds) {
|
||||
self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self else { return }
|
||||
self.statusLabel.text = msg
|
||||
// Complete shortly after showing status.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + TransportConfig.uiShareExtensionDismissDelaySeconds) { [weak self] in
|
||||
self?.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user