Share extension (#3506)

* Setup simple share extension

* Switch the app url scheme to be the full bundle identifier

* Setup a share extension that show a SwiftUI view, uses rust tracing and redirects to the hosting aplication

* Move media as json through the custom scheme into the main app and deep link into the media upload preview screen

* Fix message forwarding and global search screen room summary provider filtering.

* Tweak the message forwarding and global search screen designs.

* Add a room selection screen to use after receiving a share request from the share extension

* Fix share extension entitlements

* Share the temporary directory between the main app and the extensions; rename the caches one.

* Remove the no longer needed notification avatar flipping fix.

* Extract the placeholder avatar image generator from the NSE

* Nest `AvatarSize` within the new `Avatars` enum

* Donate an `INSendMessageIntent` to the system every time we send a message so they appear as share suggestions

* Support suggestions in the share extension itself

* Improve sharing animations and fix presentation when room already on the stack

* Clear all routes when sharing without a preselected room.

* Fix broken unit tests

* Various initial tweaks following code review.

* Correctly clean up and dismiss the share extension for all paths.

* Move the share extension path to a constants enum

* Rename UserSessionFlowCoordinator specific share extension states and events

* Add UserSession and Room flow coordinator share route tests

* Tweak the share extension logic.
This commit is contained in:
Stefan Ceriu
2024-11-13 14:02:47 +02:00
committed by GitHub
parent 3a600a94d5
commit b122b02bee
69 changed files with 1583 additions and 272 deletions
@@ -183,37 +183,17 @@ extension UNMutableNotificationContent {
@MainActor
private func getPlaceholderAvatarImageData(name: String, id: String) async -> Data? {
// The version value is used in case the design of the placeholder is updated to force a replacement
let shouldFlipAvatar = shouldFlipAvatar()
let prefix = "notification_placeholder\(shouldFlipAvatar ? "V9F" : "V9")"
let prefix = "notification_placeholderV9"
let fileName = "\(prefix)_\(name)_\(id).png"
if let data = try? Data(contentsOf: URL.temporaryDirectory.appendingPathComponent(fileName)) {
MXLog.info("Found existing notification icon placeholder")
return data
}
MXLog.info("Generating notification icon placeholder")
let image = PlaceholderAvatarImage(name: name,
contentID: id)
.clipShape(Circle())
.frame(width: 50, height: 50)
let renderer = ImageRenderer(content: image)
// Specify the scale so the image is rendered correctly. We don't have access to the screen
// here so a hardcoded 3.0 will have to do
renderer.scale = 3.0
guard let image = renderer.uiImage else {
MXLog.info("Generating notification icon placeholder failed")
return nil
}
let data: Data?
if shouldFlipAvatar {
data = image.flippedVertically().pngData()
} else {
data = image.pngData()
}
let data = Avatars.generatePlaceholderAvatarImageData(name: name, id: id, size: .init(width: 50, height: 50))
if let data {
do {
@@ -224,44 +204,7 @@ extension UNMutableNotificationContent {
return data
}
}
return data
}
/// On simulators and macOS the image is rendered correctly
/// On devices before iOS 17 and iOS 17.2.0 it's rendered upside down and needs to be flipped
/// On all other versions it's rendered correctly and **doesn't** need to be flipped
private func shouldFlipAvatar() -> Bool {
#if targetEnvironment(simulator)
return false
#else
if ProcessInfo.processInfo.isiOSAppOnMac {
return false
}
guard let version = Version(UIDevice.current.systemVersion) else {
return false
}
if version < Version(17, 0, 0) {
return true
}
if version == Version(17, 2, 0) {
return true
}
return false
#endif
}
}
private extension UIImage {
func flippedVertically() -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
return UIGraphicsImageRenderer(size: size, format: format).image { context in
context.cgContext.concatenate(CGAffineTransform(scaleX: 1, y: -1))
self.draw(at: CGPoint(x: 0, y: -size.height))
}
}
}