Skip to content

Commit e52423c

Browse files
andrehgdiasmurilopereirame
authored andcommitted
[IOS] Fix app stuck after cancel import ICS
Co-authored-by: mup <[email protected]>
1 parent 6d3bbb3 commit e52423c

File tree

17 files changed

+1457
-389
lines changed

17 files changed

+1457
-389
lines changed

app-android/app/src/main/java/de/tutao/tutanota/AndroidFileFacade.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ class AndroidFileFacade(
7575
return outputFile.toUri().toString()
7676
}
7777

78-
override suspend fun openFileChooser(boundingRect: IpcClientRect, filter: List<String>?): List<String> {
78+
override suspend fun openFileChooser(
79+
boundingRect: IpcClientRect,
80+
filter: List<String>?,
81+
isFileOnly: Boolean?
82+
): List<String> {
7983
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
8084
type = "*/*"
8185
addCategory(Intent.CATEGORY_OPENABLE)

app-android/calendar/src/main/java/de/tutao/calendar/AndroidFileFacade.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ class AndroidFileFacade(
7575
return outputFile.toUri().toString()
7676
}
7777

78-
override suspend fun openFileChooser(boundingRect: IpcClientRect, filter: List<String>?): List<String> {
78+
override suspend fun openFileChooser(
79+
boundingRect: IpcClientRect,
80+
filter: List<String>?,
81+
isFileOnly: Boolean?
82+
): List<String> {
7983
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
8084
type = "*/*"
8185
addCategory(Intent.CATEGORY_OPENABLE)
@@ -387,7 +391,7 @@ class AndroidFileFacade(
387391
while (chunk * maxChunkSizeBytes <= fileSize) {
388392
val tmpFilename = Integer.toHexString(file.hashCode()) + "." + chunk + ".blob"
389393
val chunkedInputStream = BoundedInputStream.builder()
390-
.setInputStream(inputStream)
394+
.setInputStream(inputStream)
391395
.setMaxCount(maxChunkSizeBytes.toLong())
392396
.get()
393397
val tmpFile = File(tempDir.decrypt, tmpFilename)

app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/FileFacade.kt

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface FileFacade {
2323
suspend fun openFileChooser(
2424
boundingRect: IpcClientRect,
2525
filter: List<String>?,
26+
isFileOnly: Boolean?,
2627
): List<String>
2728
/**
2829
* Opens OS file picker for selecting a folder. Only on desktop.

app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/FileFacadeReceiveDispatcher.kt

+2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ class FileFacadeReceiveDispatcher(
2626
"openFileChooser" -> {
2727
val boundingRect: IpcClientRect = json.decodeFromString(arg[0])
2828
val filter: List<String>? = json.decodeFromString(arg[1])
29+
val isFileOnly: Boolean? = json.decodeFromString(arg[2])
2930
val result: List<String> = this.facade.openFileChooser(
3031
boundingRect,
3132
filter,
33+
isFileOnly,
3234
)
3335
return json.encodeToString(result)
3436
}

app-ios/TutanotaSharedFramework/GeneratedIpc/FileFacade.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public protocol FileFacade {
1919
*/
2020
func openFileChooser(
2121
_ boundingRect: IpcClientRect,
22-
_ filter: [String]?
22+
_ filter: [String]?,
23+
_ isFileOnly: Bool?
2324
) async throws -> [String]
2425
/**
2526
* Opens OS file picker for selecting a folder. Only on desktop.

app-ios/TutanotaSharedFramework/GeneratedIpc/FileFacadeReceiveDispatcher.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ public class FileFacadeReceiveDispatcher {
2121
case "openFileChooser":
2222
let boundingRect = try! JSONDecoder().decode(IpcClientRect.self, from: arg[0].data(using: .utf8)!)
2323
let filter = try! JSONDecoder().decode([String]?.self, from: arg[1].data(using: .utf8)!)
24+
let isFileOnly = try! JSONDecoder().decode(Bool?.self, from: arg[2].data(using: .utf8)!)
2425
let result = try await self.facade.openFileChooser(
2526
boundingRect,
26-
filter
27+
filter,
28+
isFileOnly
2729
)
2830
return toJson(result)
2931
case "openFolderChooser":

app-ios/calendar/Sources/Files/FileChooser.swift

+43-39
Original file line numberDiff line numberDiff line change
@@ -26,69 +26,73 @@ class TUTFileChooser: NSObject, UIImagePickerControllerDelegate, UINavigationCon
2626
imagePickerController.delegate = self
2727
}
2828

29-
@MainActor public func open(withAnchorRect anchorRect: CGRect) async throws -> [String] {
29+
@MainActor public func open(withAnchorRect anchorRect: CGRect, isFileOnly: Bool) async throws -> [String] {
3030
if let previousHandler = resultHandler {
3131
TUTSLog("Another file picker is already open?")
3232
sourceController.dismiss(animated: true, completion: nil)
3333
previousHandler(.success([]))
3434
}
3535

36-
let attachmentTypeMenu = UIDocumentMenuViewController(documentTypes: supportedUTIs, in: UIDocumentPickerMode.import)
37-
38-
self.attachmentTypeMenu = attachmentTypeMenu
39-
40-
attachmentTypeMenu.delegate = self
36+
var filePicker: UIDocumentPickerViewController?
37+
if isFileOnly {
38+
filePicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.content, UTType.archive, UTType.data], asCopy: true)
39+
filePicker!.delegate = self
40+
} else {
41+
self.attachmentTypeMenu = UIDocumentMenuViewController(documentTypes: supportedUTIs, in: UIDocumentPickerMode.import)
42+
self.attachmentTypeMenu!.delegate = self
43+
}
4144

4245
// add menu item for selecting images from photo library.
4346
// according to developer documentation check if the source type is available first https://developer.apple.com/reference/uikit/uiimagepickercontroller
44-
if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) {
47+
if !isFileOnly && UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) {
4548
if UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad {
46-
attachmentTypeMenu.modalPresentationStyle = .popover
47-
popOverPresentationController = attachmentTypeMenu.popoverPresentationController
49+
filePicker?.modalPresentationStyle = .popover
50+
popOverPresentationController = filePicker?.popoverPresentationController
4851
popOverPresentationController?.permittedArrowDirections = [.up, .down]
4952
popOverPresentationController?.sourceView = sourceController.view
5053
popOverPresentationController?.sourceRect = anchorRect
5154
}
5255
let photosLabel = translate("TutaoChoosePhotosAction", default: "Photos")
53-
attachmentTypeMenu.addOption(
54-
withTitle: photosLabel,
55-
image: photoLibImage,
56-
order: .first,
57-
handler: { [weak self] in
58-
// capture the weak reference to avoid reference self
59-
guard let self else { return }
60-
61-
// No need to ask for permissions with new picker
62-
if #available(iOS 14.0, *) {
63-
self.showPhpicker(anchor: anchorRect)
64-
} else {
65-
// ask for permission because of changed behaviour in iOS 11
66-
if PHPhotoLibrary.authorizationStatus() == .notDetermined {
67-
PHPhotoLibrary.requestAuthorization({ status in
68-
if status == .authorized { self.showLegacyImagePicker(anchor: anchorRect) } else { self.sendResult(filePath: nil) }
69-
})
70-
} else if PHPhotoLibrary.authorizationStatus() == .authorized {
71-
self.showLegacyImagePicker(anchor: anchorRect)
56+
self.attachmentTypeMenu!
57+
.addOption(
58+
withTitle: photosLabel,
59+
image: photoLibImage,
60+
order: .first,
61+
handler: { [weak self] in
62+
// capture the weak reference to avoid reference self
63+
guard let self else { return }
64+
65+
// No need to ask for permissions with new picker
66+
if #available(iOS 14.0, *) {
67+
self.showPhpicker(anchor: anchorRect)
7268
} else {
73-
self.showPermissionDeniedDialog()
69+
// ask for permission because of changed behaviour in iOS 11
70+
if PHPhotoLibrary.authorizationStatus() == .notDetermined {
71+
PHPhotoLibrary.requestAuthorization({ status in
72+
if status == .authorized { self.showLegacyImagePicker(anchor: anchorRect) } else { self.sendResult(filePath: nil) }
73+
})
74+
} else if PHPhotoLibrary.authorizationStatus() == .authorized {
75+
self.showLegacyImagePicker(anchor: anchorRect)
76+
} else {
77+
self.showPermissionDeniedDialog()
78+
}
7479
}
7580
}
76-
}
77-
)
81+
)
7882
}
7983

8084
// add menu item for opening the camera and take a photo or video.
8185
// according to developer documentation check if the source type is available first https://developer.apple.com/reference/uikit/uiimagepickercontroller
82-
if UIImagePickerController.isSourceTypeAvailable(.camera) {
86+
if !isFileOnly && UIImagePickerController.isSourceTypeAvailable(.camera) {
8387
let cameraLabel = translate("TutaoShowCameraAction", default: "Camera")
8488

8589
// capture the weak reference to avoid reference cycle
86-
attachmentTypeMenu.addOption(withTitle: cameraLabel, image: cameraImage, order: .first) { [weak self] in self?.openCamera() }
90+
self.attachmentTypeMenu!.addOption(withTitle: cameraLabel, image: cameraImage, order: .first) { [weak self] in self?.openCamera() }
8791
}
8892

8993
return try await withCheckedThrowingContinuation { continuation in
9094
resultHandler = continuation.resume(with:)
91-
sourceController.present(attachmentTypeMenu, animated: true, completion: nil)
95+
sourceController.present((isFileOnly ? filePicker : self.attachmentTypeMenu)!, animated: true, completion: nil)
9296
}
9397
}
9498

@@ -311,18 +315,18 @@ class TUTFileChooser: NSObject, UIImagePickerControllerDelegate, UINavigationCon
311315
}
312316

313317
/**
314-
* Replace ".heic" or ".heif" extensions with ".jpeg".
315-
*/
318+
* Replace ".heic" or ".heif" extensions with ".jpeg".
319+
*/
316320
private func changeExtensionToJpeg(filename: URL) -> URL { filename.deletingPathExtension().appendingPathExtension("jpg") }
317321
}
318322

319323
/**
320-
Extending TUTFileChooser on iOS14 to conform to ickerViewControllerDelegate
324+
Extending TUTFileChooser on iOS14 to conform to ickerViewControllerDelegate
321325
*/
322326
@available(iOS 14.0, *) extension TUTFileChooser: PHPickerViewControllerDelegate {
323327
/**
324-
Invoked when user finished picking the files.
325-
*/
328+
Invoked when user finished picking the files.
329+
*/
326330
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
327331
picker.dismiss(animated: true, completion: nil)
328332

app-ios/calendar/Sources/Files/IosFileFacade.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ class IosFileFacade: FileFacade {
2828

2929
func open(_ location: String, _ mimeType: String) async throws { await self.viewer.openFile(path: location) }
3030

31-
func openFileChooser(_ boundingRect: IpcClientRect, _ filter: [String]?) async throws -> [String] {
31+
func openFileChooser(_ boundingRect: IpcClientRect, _ filter: [String]?, _ isFileOnly: Bool? = false) async throws -> [String] {
3232
let anchor = CGRect(x: boundingRect.x, y: boundingRect.y, width: boundingRect.width, height: boundingRect.height)
33-
let files = try await self.chooser.open(withAnchorRect: anchor)
33+
let files = try await self.chooser.open(withAnchorRect: anchor, isFileOnly: isFileOnly!)
3434
var returnfiles = [String]()
3535
for file in files {
3636
let fileUrl = URL(fileURLWithPath: file)

app-ios/tutanota/Sources/Files/FileChooser.swift

+38-34
Original file line numberDiff line numberDiff line change
@@ -26,69 +26,73 @@ class TUTFileChooser: NSObject, UIImagePickerControllerDelegate, UINavigationCon
2626
imagePickerController.delegate = self
2727
}
2828

29-
@MainActor public func open(withAnchorRect anchorRect: CGRect) async throws -> [String] {
29+
@MainActor public func open(withAnchorRect anchorRect: CGRect, isFileOnly: Bool) async throws -> [String] {
3030
if let previousHandler = resultHandler {
3131
TUTSLog("Another file picker is already open?")
3232
sourceController.dismiss(animated: true, completion: nil)
3333
previousHandler(.success([]))
3434
}
3535

36-
let attachmentTypeMenu = UIDocumentMenuViewController(documentTypes: supportedUTIs, in: UIDocumentPickerMode.import)
37-
38-
self.attachmentTypeMenu = attachmentTypeMenu
39-
40-
attachmentTypeMenu.delegate = self
36+
var filePicker: UIDocumentPickerViewController?
37+
if isFileOnly {
38+
filePicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.content, UTType.archive, UTType.data], asCopy: true)
39+
filePicker!.delegate = self
40+
} else {
41+
self.attachmentTypeMenu = UIDocumentMenuViewController(documentTypes: supportedUTIs, in: UIDocumentPickerMode.import)
42+
self.attachmentTypeMenu!.delegate = self
43+
}
4144

4245
// add menu item for selecting images from photo library.
4346
// according to developer documentation check if the source type is available first https://developer.apple.com/reference/uikit/uiimagepickercontroller
44-
if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) {
47+
if !isFileOnly && UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) {
4548
if UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad {
46-
attachmentTypeMenu.modalPresentationStyle = .popover
47-
popOverPresentationController = attachmentTypeMenu.popoverPresentationController
49+
filePicker?.modalPresentationStyle = .popover
50+
popOverPresentationController = filePicker?.popoverPresentationController
4851
popOverPresentationController?.permittedArrowDirections = [.up, .down]
4952
popOverPresentationController?.sourceView = sourceController.view
5053
popOverPresentationController?.sourceRect = anchorRect
5154
}
5255
let photosLabel = translate("TutaoChoosePhotosAction", default: "Photos")
53-
attachmentTypeMenu.addOption(
54-
withTitle: photosLabel,
55-
image: photoLibImage,
56-
order: .first,
57-
handler: { [weak self] in
58-
// capture the weak reference to avoid reference self
59-
guard let self else { return }
60-
61-
// No need to ask for permissions with new picker
62-
if #available(iOS 14.0, *) {
63-
self.showPhpicker(anchor: anchorRect)
64-
} else {
65-
// ask for permission because of changed behaviour in iOS 11
66-
if PHPhotoLibrary.authorizationStatus() == .notDetermined {
67-
PHPhotoLibrary.requestAuthorization({ status in
68-
if status == .authorized { self.showLegacyImagePicker(anchor: anchorRect) } else { self.sendResult(filePath: nil) }
69-
})
70-
} else if PHPhotoLibrary.authorizationStatus() == .authorized {
71-
self.showLegacyImagePicker(anchor: anchorRect)
56+
self.attachmentTypeMenu!
57+
.addOption(
58+
withTitle: photosLabel,
59+
image: photoLibImage,
60+
order: .first,
61+
handler: { [weak self] in
62+
// capture the weak reference to avoid reference self
63+
guard let self else { return }
64+
65+
// No need to ask for permissions with new picker
66+
if #available(iOS 14.0, *) {
67+
self.showPhpicker(anchor: anchorRect)
7268
} else {
73-
self.showPermissionDeniedDialog()
69+
// ask for permission because of changed behaviour in iOS 11
70+
if PHPhotoLibrary.authorizationStatus() == .notDetermined {
71+
PHPhotoLibrary.requestAuthorization({ status in
72+
if status == .authorized { self.showLegacyImagePicker(anchor: anchorRect) } else { self.sendResult(filePath: nil) }
73+
})
74+
} else if PHPhotoLibrary.authorizationStatus() == .authorized {
75+
self.showLegacyImagePicker(anchor: anchorRect)
76+
} else {
77+
self.showPermissionDeniedDialog()
78+
}
7479
}
7580
}
76-
}
77-
)
81+
)
7882
}
7983

8084
// add menu item for opening the camera and take a photo or video.
8185
// according to developer documentation check if the source type is available first https://developer.apple.com/reference/uikit/uiimagepickercontroller
82-
if UIImagePickerController.isSourceTypeAvailable(.camera) {
86+
if !isFileOnly && UIImagePickerController.isSourceTypeAvailable(.camera) {
8387
let cameraLabel = translate("TutaoShowCameraAction", default: "Camera")
8488

8589
// capture the weak reference to avoid reference cycle
86-
attachmentTypeMenu.addOption(withTitle: cameraLabel, image: cameraImage, order: .first) { [weak self] in self?.openCamera() }
90+
self.attachmentTypeMenu!.addOption(withTitle: cameraLabel, image: cameraImage, order: .first) { [weak self] in self?.openCamera() }
8791
}
8892

8993
return try await withCheckedThrowingContinuation { continuation in
9094
resultHandler = continuation.resume(with:)
91-
sourceController.present(attachmentTypeMenu, animated: true, completion: nil)
95+
sourceController.present((isFileOnly ? filePicker : self.attachmentTypeMenu)!, animated: true, completion: nil)
9296
}
9397
}
9498

app-ios/tutanota/Sources/Files/IosFileFacade.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ class IosFileFacade: FileFacade {
2828

2929
func open(_ location: String, _ mimeType: String) async throws { await self.viewer.openFile(path: location) }
3030

31-
func openFileChooser(_ boundingRect: IpcClientRect, _ filter: [String]?) async throws -> [String] {
31+
func openFileChooser(_ boundingRect: IpcClientRect, _ filter: [String]?, _ isFileOnly: Bool? = false) async throws -> [String] {
3232
let anchor = CGRect(x: boundingRect.x, y: boundingRect.y, width: boundingRect.width, height: boundingRect.height)
33-
let files = try await self.chooser.open(withAnchorRect: anchor)
33+
let files = try await self.chooser.open(withAnchorRect: anchor, isFileOnly: isFileOnly!)
3434
var returnfiles = [String]()
3535
for file in files {
3636
let fileUrl = URL(fileURLWithPath: file)

ipc-schema/facades/FileFacade.json

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
},
2626
{
2727
"filter": "List<string>?"
28+
},
29+
{
30+
"isFileOnly": "boolean?"
2831
}
2932
],
3033
"ret": "List<string>"

0 commit comments

Comments
 (0)