From f1fac7b68ad2cc4230c205b5398e158f3129bd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20H=C3=BChne?= Date: Wed, 16 Mar 2022 11:41:16 +0100 Subject: [PATCH] [milestone/11.9.0] Milestone 11.9.0 (#1071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new version number 11.9.0, enabled beta build and warning * [feature/infinite-propfind] Infinite PROPFIND support (#1002) * - ProgressIndicatorViewController : extend with Progress object and cancellation support / monitoring - BookmarkViewController: add infinite PROPFIND prepopulation step after account creation - update SDK to provide infinite PROPFIND support * Configuration documentation updated * - update SDK to gain access to streaming infinite PROPFIND - switch from receive+parse to stream+parse for infinite PROPFIND * - update SDK to gain access to new supportsInfinitePropfind capability - BookmarkViewController: make prepopulation configurable, using new capability and new class setting - ProgressIndicatorViewController: add support for new title label view * - BookmarkViewController: fix property name typo - scheme: add new options to test streaming and split bookmark prepopulation * Configuration documentation updated * added additional build information to the Branding.plist file: - ios-app and ios-sdk commit hash - copy the Branding.plist with additional app and build information as file name * [feature/biometrical-mdm-setting ]Suggest Biometrical Unlock (#1046) * Suggest biometrical unlock, after setting up passcode * - do not show Cancel button, if password is enforced - update biometrical UI switch value in settings view - fixed bug, dismissing the view, if biometrical is not available * moved extension into ownCloudAppShared folder * Branding: (#1045) - add new `branding.user-defaults-default-values` class settings key to allow registration of alternative defaults for user defaults * - fix bug where a quick access entry showed no items when selected a second time. (#1044) * used better wording for "Cellular transfers" * - adjust to final capabilities naming for infinite PROPFIND support * upgraded app version numbers Co-authored-by: felix-schwarz Co-authored-by: Matthias Hühne Co-authored-by: Matthias Hühne * [feature/poll-efficiency] Poll for changes efficiency enhancements (#1043) * - change text in the footer below private links in accordance with https://github.com/owncloud/enterprise/issues/4726 * - SDK update * - ClientRootViewController: start OCFileProviderServiceStandby only - after the connection status is online (== authentication successful) - OR 5 seconds after receiving the core from the Core Manager - fix theoretical bug in ClientRootViewController where OCFileProviderServiceStandby could be started and not stopped if Core Manager returned a core with error * - bump version to 11.7.2 / build 202 * - FileProviderExtension: - handle case where OCDatabase is not available or not open (previously led to a hang/crash as the FP waited for a DB call to return) - disable malloc stack logging as it leads to immediate crashes when debugging the file provider * - update SDK to new version with support for coordinated scan for changes * - bump build number to 203 Co-authored-by: Matthias Hühne * Configuration documentation updated * [fix/media-playback-url] Fix WebDAV endpoint URL for media playback after restoration (#1093) * - fix https://github.com/owncloud/enterprise/issues/4924 via SDK update * Configuration documentation updated Co-authored-by: Matthias Hühne Co-authored-by: hosy * [feature/webfinger] Webfinger / server location (#1059) * - change text in the footer below private links in accordance with https://github.com/owncloud/enterprise/issues/4726 * - SDK update * added additional build information to the Branding.plist file: - ios-app and ios-sdk commit hash - copy the Branding.plist with additional app and build information as file name * [feature/biometrical-mdm-setting ]Suggest Biometrical Unlock (#1046) * Suggest biometrical unlock, after setting up passcode * - do not show Cancel button, if password is enforced - update biometrical UI switch value in settings view - fixed bug, dismissing the view, if biometrical is not available * moved extension into ownCloudAppShared folder * Branding: (#1045) - add new `branding.user-defaults-default-values` class settings key to allow registration of alternative defaults for user defaults * - fix bug where a quick access entry showed no items when selected a second time. (#1044) * used better wording for "Cellular transfers" * - in branded clients: removed UISwitch to enable/disable deletion of available offline files in manage screen - fixed text center layout in table view cell * fixed UI issue in sharing view * #4801 "Log out" text changes * #4801 text changes for "Prevent gestures" * - ThemeTableViewCell, StaticTableViewRow: new .plain message style (#1053) - add new view controller to present license texts for each component individually * [feature/modular-localization] Modular localization (#1054) * - Branding class: - move logic for VendorServices.appName to Branding.appDisplayName - change app.name variable value to that of Branding.appDisplayName - switch String.localized from NSLocalizedString() to OCLocale.localize() - SwiftLint: adapt to latest SwiftLint version * - add .localized(replacements) function to provide easy access to custom variables support in OCLocale * - update SDK * [feature/mdm-auto-connect] Skip Account Screen via MDM (#1056) * #4801 Skip "Manage" screen / automatically open "Files" screen after login via MDM parameter account.auto-connect * - SceneDelegate: use .first (which returns nil in case there are no bookmarks) instead of bookmark(at: 0) (which crashes in case there are no bookmarks because the array is empty and the method tries to access an item at index 0) * added auto-connect after every setup, not only the first setup Co-authored-by: Felix Schwarz * [feature/mdm-biometrical-unlock] MDM setting for Biometrical Unlock (#1058) * #4818 control via MDM to auto enable biometrical unlock * overwrites user defaults with MDM settings and use existing getter/setter; renamed MDM key to use-biometrical-unlock * - use user defaults for use-biometrical-unlock - show Face ID authorization immediately after enabling Face ID * - StaticLoginSetupViewController: add support for OCServerLocator, requesting a username if it is enabled - add additional debug options to ownCloud.xcscheme - update SDK to gain access to OCServerLocator * updated APP_VERSION number * reverted merge conflict changes * using correct SDK commit * using latest development sdk commit Co-authored-by: Matthias Hühne Co-authored-by: Matthias Hühne * changed app version number * added missing translation string * [feature/rename-accounts] Rename Account without Authentication Flow (#1097) * #972 rename account name only (bookmark) is possible without re-authentication flow * added changelog entry * moved duplicated code into a function for reuse * fixed issue link in changelog entry * Calens changelog updated * fixed code review finding * fixed QA findings 1,2,3 - show UI to delete authentication data in edit mode - only proceed without authentication, if authentication data is not nil * show delete authentication data UI in all edit mode calls Co-authored-by: hosy * #5008 (2) only show "Settings" toolbar, if no account is configured, otherwise "Settings" would appear twice * [feature/biometrical-button] Biometrical Authentication Button (#1098) * #1004 added biometrical unlock button, which appears by default as fallback, if biometrical unlock does not work (in file provider or not detected) * - added fallback image for face-id too - added changelog entry * Calens changelog updated * - fixed visibility of biometrical button in edit mode and, if feature is not enabled - removed unneeded code * - AppLockManager: add new property biometricCancelLabel to provide a label for the Cancel-option when presenting biometric authentication - FileProviderUI / DocumentActionViewController: use new property to display "Cancel" instead of "Enter code" for biometric authentication Co-authored-by: hosy Co-authored-by: Felix Schwarz * Calens changelog updated * removed branding test values * Transifex translation updates (#994) * disabled beta warning, beta build * - added accessibility label to biometrical unlock button - added release notes - added missing changelog files, moved to release folder * Calens changelog updated * added fastlane release notes * - update SDK to include fix/token-timetolerance changes from the SDK * changed folder name to release date * Calens changelog updated * - added missing changelog entry - changed enterprise issue to ios-app pr * Calens changelog updated * fixed duplicated app_version number on TestFlight Co-authored-by: Felix Schwarz Co-authored-by: felix-schwarz Co-authored-by: hosy Co-authored-by: Matthias Hühne <> --- CHANGELOG.md | 70 ++++++++ changelog/11.9.0_2022-03-16/1004 | 5 + changelog/11.9.0_2022-03-16/1043 | 5 + changelog/11.9.0_2022-03-16/1059 | 5 + changelog/11.9.0_2022-03-16/1093 | 5 + changelog/11.9.0_2022-03-16/1105 | 5 + changelog/11.9.0_2022-03-16/950 | 5 + changelog/11.9.0_2022-03-16/972 | 5 + doc/CONFIGURATION.json | 27 +++ docs/modules/ROOT/pages/ios_mdm_tables.adoc | 21 +++ fastlane/metadata-emm/en-US/release_notes.txt | 14 +- .../en-US/release_notes.txt | 14 +- fastlane/metadata/en-US/release_notes.txt | 14 +- ios-sdk | 2 +- .../DocumentActionViewController.swift | 1 + .../FileProviderExtension.m | 20 ++- .../ShareViewController.swift | 2 +- ownCloud.xcodeproj/project.pbxproj | 24 +-- .../xcschemes/ownCloud File Provider.xcscheme | 12 -- .../xcshareddata/xcschemes/ownCloud.xcscheme | 22 ++- .../Bookmarks/BookmarkViewController.swift | 170 +++++++++++------- .../Client/ClientRootViewController.swift | 27 ++- ownCloud/Release Notes/ReleaseNotes.plist | 47 +++++ .../Resources/Assets.xcassets/Contents.json | 6 +- .../biometrical-faceid.imageset/Contents.json | 21 +++ .../biometrical-faceid.pdf | Bin 0 -> 4657 bytes .../Contents.json | 24 +++ .../biometrical.pdf | Bin 0 -> 5494 bytes .../Resources/ar.lproj/Localizable.strings | Bin 79172 -> 82040 bytes .../Resources/de.lproj/Localizable.strings | Bin 88600 -> 91618 bytes ownCloud/Resources/el.lproj/InfoPlist.strings | Bin 2182 -> 3158 bytes .../Resources/en.lproj/Localizable.strings | 5 + .../Resources/es.lproj/Localizable.strings | Bin 87284 -> 90022 bytes .../Resources/fr.lproj/Localizable.strings | Bin 90554 -> 93460 bytes .../Resources/gl.lproj/Localizable.strings | Bin 88202 -> 90996 bytes .../Resources/he.lproj/Localizable.strings | Bin 76660 -> 79602 bytes .../Resources/pt-BR.lproj/Localizable.strings | Bin 87038 -> 90066 bytes .../Resources/ru.lproj/Localizable.strings | Bin 87952 -> 90686 bytes .../Resources/sq.lproj/Localizable.strings | Bin 86662 -> 89620 bytes .../Resources/th-TH.lproj/Localizable.strings | Bin 79206 -> 82020 bytes .../Resources/zh_TW.lproj/Localizable.strings | Bin 64618 -> 65414 bytes .../ServerListTableViewController.swift | 6 +- .../StaticLoginSetupViewController.swift | 66 ++++++- ...ingleAccountServerListViewController.swift | 2 +- .../Interface/StaticLoginViewController.swift | 3 +- .../AppLock/AppLockManager.swift | 23 ++- .../AppLock/PasscodeViewController.swift | 29 ++- .../AppLock/PasscodeViewController.xib | 34 +++- .../UIKit Extension/LAContext+Extension.swift | 23 ++- .../ProgressIndicatorViewController.swift | 95 +++++++++- .../Theme/UI/ThemeTableViewCell.swift | 4 + 51 files changed, 728 insertions(+), 135 deletions(-) create mode 100644 changelog/11.9.0_2022-03-16/1004 create mode 100644 changelog/11.9.0_2022-03-16/1043 create mode 100644 changelog/11.9.0_2022-03-16/1059 create mode 100644 changelog/11.9.0_2022-03-16/1093 create mode 100644 changelog/11.9.0_2022-03-16/1105 create mode 100644 changelog/11.9.0_2022-03-16/950 create mode 100644 changelog/11.9.0_2022-03-16/972 create mode 100644 ownCloud/Resources/Assets.xcassets/biometrical-faceid.imageset/Contents.json create mode 100644 ownCloud/Resources/Assets.xcassets/biometrical-faceid.imageset/biometrical-faceid.pdf create mode 100644 ownCloud/Resources/Assets.xcassets/biometrical-touchid.imageset/Contents.json create mode 100644 ownCloud/Resources/Assets.xcassets/biometrical-touchid.imageset/biometrical.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 22a65778a..f8b6d9db5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,73 @@ +Changelog for ownCloud iOS Client [11.9.0] (2022-03-16) +======================================= +The following sections list the changes in ownCloud iOS Client 11.9.0 relevant to +ownCloud admins and users. + +[11.9.0]: https://github.com/owncloud/ios-app/compare/milestone/11.8.2...milestone/11.9.0 + +Summary +------- + +* Bugfix - Fix WebDAV endpoint URL for media playback after restoration: [#1093](https://github.com/owncloud/ios-app/pull/1093) +* Bugfix - OAuth token renewal race condition: [#1105](https://github.com/owncloud/ios-app/pull/1105) +* Change - Biometrical Authentication Button: [#1004](https://github.com/owncloud/ios-app/issues/1004) +* Change - Poll for changes efficiency enhancements: [#1043](https://github.com/owncloud/ios-app/pull/1043) +* Change - Webfinger / server location: [#1059](https://github.com/owncloud/ios-app/pull/1059) +* Change - Infinite PROPFIND support: [#950](https://github.com/owncloud/ios-app/issues/950) +* Change - Rename Account (without re-authentication): [#972](https://github.com/owncloud/ios-app/issues/972) + +Details +------- + +* Bugfix - Fix WebDAV endpoint URL for media playback after restoration: [#1093](https://github.com/owncloud/ios-app/pull/1093) + + Fixes a bug where media playback failed with a 404 Not Found error after restoration because the + WebDAV endpoint URL was constructed from authentication data rather than OC user endpoint + data. + + https://github.com/owncloud/ios-app/pull/1093 + +* Bugfix - OAuth token renewal race condition: [#1105](https://github.com/owncloud/ios-app/pull/1105) + + Retry requests that failed with a 401 during a token refresh + + https://github.com/owncloud/ios-app/pull/1105 + +* Change - Biometrical Authentication Button: [#1004](https://github.com/owncloud/ios-app/issues/1004) + + Added biometrical authentication button to provide a fallback for the fileprovider or app, if + the automatically biometrical unlock does not work, or the user cancel the biometrical + authentication flow. + + https://github.com/owncloud/ios-app/issues/1004 + +* Change - Poll for changes efficiency enhancements: [#1043](https://github.com/owncloud/ios-app/pull/1043) + + Avoids simultaneous polling for changes by FileProvider and app. + + https://github.com/owncloud/ios-app/pull/1043 + +* Change - Webfinger / server location: [#1059](https://github.com/owncloud/ios-app/pull/1059) + + Allows using webfinger or a lookup table to locate and use an alternative server based on the + user name + + https://github.com/owncloud/ios-app/pull/1059 + +* Change - Infinite PROPFIND support: [#950](https://github.com/owncloud/ios-app/issues/950) + + Added support for prepopulation of newly created account bookmarks via infinite PROPFINDs, + which speeds up the initial scan + + https://github.com/owncloud/ios-app/issues/950 + +* Change - Rename Account (without re-authentication): [#972](https://github.com/owncloud/ios-app/issues/972) + + Check if only the account name was changed in edit mode: save and dismiss without + re-authentication + + https://github.com/owncloud/ios-app/issues/972 + Changelog for ownCloud iOS Client [11.8.2] (2022-01-17) ======================================= The following sections list the changes in ownCloud iOS Client 11.8.2 relevant to diff --git a/changelog/11.9.0_2022-03-16/1004 b/changelog/11.9.0_2022-03-16/1004 new file mode 100644 index 000000000..8d3d5e7eb --- /dev/null +++ b/changelog/11.9.0_2022-03-16/1004 @@ -0,0 +1,5 @@ +Change: Biometrical Authentication Button + +Added biometrical authentication button to provide a fallback for the fileprovider or app, if the automatically biometrical unlock does not work, or the user cancel the biometrical authentication flow. + +https://github.com/owncloud/ios-app/issues/1004 diff --git a/changelog/11.9.0_2022-03-16/1043 b/changelog/11.9.0_2022-03-16/1043 new file mode 100644 index 000000000..448636ab8 --- /dev/null +++ b/changelog/11.9.0_2022-03-16/1043 @@ -0,0 +1,5 @@ +Change: Poll for changes efficiency enhancements + +Avoids simultaneous polling for changes by FileProvider and app. + +https://github.com/owncloud/ios-app/pull/1043 diff --git a/changelog/11.9.0_2022-03-16/1059 b/changelog/11.9.0_2022-03-16/1059 new file mode 100644 index 000000000..d9a360d0b --- /dev/null +++ b/changelog/11.9.0_2022-03-16/1059 @@ -0,0 +1,5 @@ +Change: Webfinger / server location + +Allows using webfinger or a lookup table to locate and use an alternative server based on the user name + +https://github.com/owncloud/ios-app/pull/1059 diff --git a/changelog/11.9.0_2022-03-16/1093 b/changelog/11.9.0_2022-03-16/1093 new file mode 100644 index 000000000..4f25dff08 --- /dev/null +++ b/changelog/11.9.0_2022-03-16/1093 @@ -0,0 +1,5 @@ +Bugfix: Fix WebDAV endpoint URL for media playback after restoration + +Fixes a bug where media playback failed with a 404 Not Found error after restoration because the WebDAV endpoint URL was constructed from authentication data rather than OC user endpoint data. + +https://github.com/owncloud/ios-app/pull/1093 diff --git a/changelog/11.9.0_2022-03-16/1105 b/changelog/11.9.0_2022-03-16/1105 new file mode 100644 index 000000000..f2fc16e70 --- /dev/null +++ b/changelog/11.9.0_2022-03-16/1105 @@ -0,0 +1,5 @@ +Bugfix: OAuth token renewal race condition + +Retry requests that failed with a 401 during a token refresh + +https://github.com/owncloud/ios-app/pull/1105 diff --git a/changelog/11.9.0_2022-03-16/950 b/changelog/11.9.0_2022-03-16/950 new file mode 100644 index 000000000..9273a01e0 --- /dev/null +++ b/changelog/11.9.0_2022-03-16/950 @@ -0,0 +1,5 @@ +Change: Infinite PROPFIND support + +Added support for prepopulation of newly created account bookmarks via infinite PROPFINDs, which speeds up the initial scan + +https://github.com/owncloud/ios-app/issues/950 diff --git a/changelog/11.9.0_2022-03-16/972 b/changelog/11.9.0_2022-03-16/972 new file mode 100644 index 000000000..0198a5e32 --- /dev/null +++ b/changelog/11.9.0_2022-03-16/972 @@ -0,0 +1,5 @@ +Change: Rename Account (without re-authentication) + +Check if only the account name was changed in edit mode: save and dismiss without re-authentication + +https://github.com/owncloud/ios-app/issues/972 diff --git a/doc/CONFIGURATION.json b/doc/CONFIGURATION.json index a84949d7d..631d8df97 100644 --- a/doc/CONFIGURATION.json +++ b/doc/CONFIGURATION.json @@ -527,6 +527,33 @@ "status" : "supported", "type" : "string" }, + { + "autoExpansion" : "none", + "category" : "Bookmarks", + "categoryTag" : "bookmarks", + "classIdentifier" : "bookmark", + "className" : "ownCloud.BookmarkViewController", + "description" : "Controls prepopulation of the local database with the full item set during account setup.", + "flatIdentifier" : "bookmark.prepopulation", + "key" : "prepopulation", + "label" : "bookmark.prepopulation", + "possibleValues" : [ + { + "description" : "No prepopulation. Request the contents of every folder individually.", + "value" : "doNot" + }, + { + "description" : "Parse the prepopulation metadata after receiving it as a whole.", + "value" : "split" + }, + { + "description" : "Parse the prepopulation metadata while receiving it.", + "value" : "streaming" + } + ], + "status" : "supported", + "type" : "string" + }, { "autoExpansion" : "none", "category" : "Bookmarks", diff --git a/docs/modules/ROOT/pages/ios_mdm_tables.adoc b/docs/modules/ROOT/pages/ios_mdm_tables.adoc index e1f49cf44..eccdb58df 100644 --- a/docs/modules/ROOT/pages/ios_mdm_tables.adoc +++ b/docs/modules/ROOT/pages/ios_mdm_tables.adoc @@ -314,6 +314,27 @@ tag::bookmarks[] |The default URL for the creation of new bookmarks. |supported `candidate` +|bookmark.prepopulation +|string +| +|Controls prepopulation of the local database with the full item set during account setup. +[cols="1,1"] +!=== +! Value +! Description +! `doNot` +! No prepopulation. Request the contents of every folder individually. + +! `split` +! Parse the prepopulation metadata after receiving it as a whole. + +! `streaming` +! Parse the prepopulation metadata while receiving it. + +!=== + +|supported `candidate` + |bookmark.url-editable |bool |`true` diff --git a/fastlane/metadata-emm/en-US/release_notes.txt b/fastlane/metadata-emm/en-US/release_notes.txt index f4c421176..353457a9c 100644 --- a/fastlane/metadata-emm/en-US/release_notes.txt +++ b/fastlane/metadata-emm/en-US/release_notes.txt @@ -1,6 +1,12 @@ -• Fix: PDF Editing -Fixed bug that prevents changes to PDFs being saved in place. +• Biometrical Authentication Button +Added a biometrical authentication button to the passcode unlock view. The biomatrical cancel button enables passcode unlock as fallback. -• Fix: Continuous Audio Playback -Fixed continuous audio playback, which stopped after two audio files. +• Rename Account +Renaming an account does no longer need a re-authentication + +• Media Playback +Fixes a bug where media playback failed + +• Faster Account Scan +We improved the time for the initial scan after setting up a new account diff --git a/fastlane/metadata-owncloud-online/en-US/release_notes.txt b/fastlane/metadata-owncloud-online/en-US/release_notes.txt index f4c421176..353457a9c 100644 --- a/fastlane/metadata-owncloud-online/en-US/release_notes.txt +++ b/fastlane/metadata-owncloud-online/en-US/release_notes.txt @@ -1,6 +1,12 @@ -• Fix: PDF Editing -Fixed bug that prevents changes to PDFs being saved in place. +• Biometrical Authentication Button +Added a biometrical authentication button to the passcode unlock view. The biomatrical cancel button enables passcode unlock as fallback. -• Fix: Continuous Audio Playback -Fixed continuous audio playback, which stopped after two audio files. +• Rename Account +Renaming an account does no longer need a re-authentication + +• Media Playback +Fixes a bug where media playback failed + +• Faster Account Scan +We improved the time for the initial scan after setting up a new account diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index f4c421176..353457a9c 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -1,6 +1,12 @@ -• Fix: PDF Editing -Fixed bug that prevents changes to PDFs being saved in place. +• Biometrical Authentication Button +Added a biometrical authentication button to the passcode unlock view. The biomatrical cancel button enables passcode unlock as fallback. -• Fix: Continuous Audio Playback -Fixed continuous audio playback, which stopped after two audio files. +• Rename Account +Renaming an account does no longer need a re-authentication + +• Media Playback +Fixes a bug where media playback failed + +• Faster Account Scan +We improved the time for the initial scan after setting up a new account diff --git a/ios-sdk b/ios-sdk index ef2a2d263..cd86774c3 160000 --- a/ios-sdk +++ b/ios-sdk @@ -1 +1 @@ -Subproject commit ef2a2d2635d5fa58a307b82884f8b9e02cef97a1 +Subproject commit cd86774c3fa58492c910ba976e1251969583a780 diff --git a/ownCloud File Provider UI/DocumentActionViewController.swift b/ownCloud File Provider UI/DocumentActionViewController.swift index 758218b71..a7d0d6cb5 100644 --- a/ownCloud File Provider UI/DocumentActionViewController.swift +++ b/ownCloud File Provider UI/DocumentActionViewController.swift @@ -153,6 +153,7 @@ class DocumentActionViewController: FPUIActionExtensionViewController { override func prepare(forError error: Error) { if AppLockManager.supportedOnDevice { AppLockManager.shared.passwordViewHostViewController = self + AppLockManager.shared.biometricCancelLabel = "Cancel".localized AppLockManager.shared.cancelAction = { [weak self] in self?.complete(cancelWith: NSError(domain: FPUIErrorDomain, code: Int(FPUIExtensionErrorCode.userCancelled.rawValue), userInfo: nil)) } diff --git a/ownCloud File Provider/FileProviderExtension.m b/ownCloud File Provider/FileProviderExtension.m index 95d1dd7a0..269bf2b8a 100644 --- a/ownCloud File Provider/FileProviderExtension.m +++ b/ownCloud File Provider/FileProviderExtension.m @@ -189,17 +189,27 @@ - (NSFileProviderItem)itemForIdentifier:(NSFileProviderItemIdentifier)identifier if ([identifier isEqual:NSFileProviderRootContainerItemIdentifier]) { // Root item - [self.core.vault.database retrieveCacheItemsAtPath:@"/" itemOnly:YES completionHandler:^(OCDatabase *db, NSError *error, OCSyncAnchor syncAnchor, NSArray *items) { - item = items.firstObject; - returnError = error; + OCDatabase *database; + + if (((database = core.vault.database) != nil) && database.isOpened) + { + [database retrieveCacheItemsAtPath:@"/" itemOnly:YES completionHandler:^(OCDatabase *db, NSError *error, OCSyncAnchor syncAnchor, NSArray *items) { + item = items.firstObject; + returnError = error; + OCSyncExecDone(itemRetrieval); + }]; + } + else + { + // Database not available OCSyncExecDone(itemRetrieval); - }]; + } } else { // Other item - [self.core retrieveItemFromDatabaseForLocalID:(OCLocalID)identifier completionHandler:^(NSError *error, OCSyncAnchor syncAnchor, OCItem *itemFromDatabase) { + [core retrieveItemFromDatabaseForLocalID:(OCLocalID)identifier completionHandler:^(NSError *error, OCSyncAnchor syncAnchor, OCItem *itemFromDatabase) { item = itemFromDatabase; returnError = error; diff --git a/ownCloud Share Extension/ShareViewController.swift b/ownCloud Share Extension/ShareViewController.swift index ed95c98c4..7c6ce31fa 100644 --- a/ownCloud Share Extension/ShareViewController.swift +++ b/ownCloud Share Extension/ShareViewController.swift @@ -200,7 +200,7 @@ class ShareViewController: MoreStaticTableViewController { OnMainThread { self.navigationController?.popToViewController(self, animated: false) - let progressViewController = ProgressIndicatorViewController(initialProgressLabel: "Preparing…".localized, cancelHandler: {}) + let progressViewController = ProgressIndicatorViewController(initialProgressLabel: "Preparing…".localized, progress: nil, cancelHandler: {}) self.present(progressViewController, animated: false) diff --git a/ownCloud.xcodeproj/project.pbxproj b/ownCloud.xcodeproj/project.pbxproj index 1dc80d25a..a92610413 100644 --- a/ownCloud.xcodeproj/project.pbxproj +++ b/ownCloud.xcodeproj/project.pbxproj @@ -4730,8 +4730,8 @@ APP_BUILD_FLAGS = "$(inherited)"; APP_BUILD_FLAGS_SWIFT = "$(APP_BUILD_FLAGS)"; APP_PRODUCT_NAME = ownCloud; - APP_SHORT_VERSION = 11.8.2; - APP_VERSION = 208; + APP_SHORT_VERSION = 11.9.0; + APP_VERSION = 211; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -4799,8 +4799,8 @@ APP_BUILD_FLAGS = "$(inherited)"; APP_BUILD_FLAGS_SWIFT = "$(APP_BUILD_FLAGS)"; APP_PRODUCT_NAME = ownCloud; - APP_SHORT_VERSION = 11.8.2; - APP_VERSION = 208; + APP_SHORT_VERSION = 11.9.0; + APP_VERSION = 211; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -4863,8 +4863,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - APP_SHORT_VERSION = 11.8.2; - APP_VERSION = 208; + APP_SHORT_VERSION = 11.9.0; + APP_VERSION = 211; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = ownCloud/ownCloud.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; @@ -4897,8 +4897,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - APP_SHORT_VERSION = 11.8.2; - APP_VERSION = 208; + APP_SHORT_VERSION = 11.9.0; + APP_VERSION = 211; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = ownCloud/ownCloud.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -5255,8 +5255,8 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - APP_SHORT_VERSION = 11.8.2; - APP_VERSION = 208; + APP_SHORT_VERSION = 11.9.0; + APP_VERSION = 211; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = NO; @@ -5299,8 +5299,8 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - APP_SHORT_VERSION = 11.8.2; - APP_VERSION = 208; + APP_SHORT_VERSION = 11.9.0; + APP_VERSION = 211; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = NO; diff --git a/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud File Provider.xcscheme b/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud File Provider.xcscheme index f96d48bbc..fa1aadec9 100644 --- a/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud File Provider.xcscheme +++ b/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud File Provider.xcscheme @@ -152,18 +152,6 @@ isEnabled = "NO"> - - - - - - + isEnabled = "NO"> + + + + + + + + Void)?) -> Void)) { + guard let userActionCompletionHandler = self.userActionCompletionHandler else { return } + + self.userActionCompletionHandler = nil + + OnMainThread { + hudCompletion({ + OnMainThread { + userActionCompletionHandler(self.bookmark, true) + } + self.presentingViewController?.dismiss(animated: true, completion: nil) + }) + } + } + // MARK: - User actions @objc func userActionCancel() { let userActionCompletionHandler = self.userActionCompletionHandler @@ -553,80 +581,72 @@ class BookmarkViewController: StaticTableViewController { save(hudCompletion: hudCompletion) } - func save(hudCompletion: @escaping (((() -> Void)?) -> Void)) { - guard let bookmark = self.bookmark else { return } - - if isBookmarkComplete(bookmark: bookmark) { - bookmark.authenticationDataStorage = .keychain // Commit auth changes to keychain - - let connection = instantiateConnection(for: bookmark) - - connection.connect { [weak self] (error, issue) in - if let strongSelf = self { - if error == nil { - bookmark.userDisplayName = connection.loggedInUser?.displayName - - connection.disconnect(completionHandler: { - switch strongSelf.mode { - case .create: - // Add bookmark - OCBookmarkManager.shared.addBookmark(bookmark) + func updateBookmark(bookmark: OCBookmark) { + originalBookmark?.setValuesFrom(bookmark) + if let originalBookmark = originalBookmark, !OCBookmarkManager.shared.updateBookmark(originalBookmark) { + Log.error("Changes to \(originalBookmark) not saved as it's not tracked by OCBookmarkManager!") + } + } - case .edit: - // Update original bookmark - self?.originalBookmark?.setValuesFrom(bookmark) - if let originalBookmark = self?.originalBookmark, !OCBookmarkManager.shared.updateBookmark(originalBookmark) { - Log.error("Changes to \(originalBookmark) not saved as it's not tracked by OCBookmarkManager!") + func save(hudCompletion: @escaping (((() -> Void)?) -> Void)) { + guard let bookmark = self.bookmark else { return } + + if isBookmarkComplete(bookmark: bookmark) { + bookmark.authenticationDataStorage = .keychain // Commit auth changes to keychain + let connection = instantiateConnection(for: bookmark) + + connection.connect { [weak self] (error, issue) in + if let strongSelf = self { + if error == nil { + bookmark.userDisplayName = connection.loggedInUser?.displayName + + connection.disconnect(completionHandler: { + switch strongSelf.mode { + case .create: + // Add bookmark + OCBookmarkManager.shared.addBookmark(bookmark) + + case .edit: + // Update original bookmark + self?.updateBookmark(bookmark: bookmark) } - } - - let userActionCompletionHandler = strongSelf.userActionCompletionHandler - strongSelf.userActionCompletionHandler = nil + strongSelf.completeAndDismiss(with: hudCompletion) + }) + } else { OnMainThread { hudCompletion({ - OnMainThread { - userActionCompletionHandler?(bookmark, true) + if let issue = issue { + self?.bookmark?.authenticationData = nil + + IssuesCardViewController.present(on: strongSelf, issue: issue, completion: { [weak self, weak issue] (response) in + switch response { + case .cancel: + issue?.reject() + + case .approve: + issue?.approve() + self?.handleContinue() + + case .dismiss: break + } + }) + } else { + strongSelf.presentingViewController?.dismiss(animated: true, completion: nil) } - strongSelf.presentingViewController?.dismiss(animated: true, completion: nil) }) } - - }) - } else { - OnMainThread { - hudCompletion({ - if let issue = issue { - self?.bookmark?.authenticationData = nil - - IssuesCardViewController.present(on: strongSelf, issue: issue, completion: { [weak self, weak issue] (response) in - switch response { - case .cancel: - issue?.reject() - - case .approve: - issue?.approve() - self?.handleContinue() - - case .dismiss: break - } - }) - } else { - strongSelf.presentingViewController?.dismiss(animated: true, completion: nil) - } - }) } } } + } else { + hudCompletion({ [weak self] in + if let strongSelf = self { + strongSelf.handleContinue() + } + }) } - } else { - hudCompletion({ [weak self] in - if let strongSelf = self { - strongSelf.handleContinue() - } - }) } - } // MARK: - Update section and row composition func composeSectionsAndRows(animated: Bool = true, completion: (() -> Void)? = nil) { @@ -918,6 +938,13 @@ extension OCClassSettingsIdentifier { extension OCClassSettingsKey { static let bookmarkDefaultURL = OCClassSettingsKey("default-url") static let bookmarkURLEditable = OCClassSettingsKey("url-editable") + static let prepopulation = OCClassSettingsKey("prepopulation") +} + +enum BookmarkPrepopulationMethod : String { + case doNot + case streaming + case split } extension BookmarkViewController : OCClassSettingsSupport { @@ -947,6 +974,27 @@ extension BookmarkViewController : OCClassSettingsSupport { .description : "Controls whether the server URL in the text field during the creation of new bookmarks can be changed.", .category : "Bookmarks", .status : OCClassSettingsKeyStatus.supported + ], + + .prepopulation : [ + .type : OCClassSettingsMetadataType.string, + .description : "Controls prepopulation of the local database with the full item set during account setup.", + .category : "Bookmarks", + .status : OCClassSettingsKeyStatus.supported, + .possibleValues : [ + [ + OCClassSettingsMetadataKey.description : "No prepopulation. Request the contents of every folder individually.", + OCClassSettingsMetadataKey.value : BookmarkPrepopulationMethod.doNot.rawValue + ], + [ + OCClassSettingsMetadataKey.description : "Parse the prepopulation metadata while receiving it.", + OCClassSettingsMetadataKey.value : BookmarkPrepopulationMethod.streaming.rawValue + ], + [ + OCClassSettingsMetadataKey.description : "Parse the prepopulation metadata after receiving it as a whole.", + OCClassSettingsMetadataKey.value : BookmarkPrepopulationMethod.split.rawValue + ] + ] ] ] } diff --git a/ownCloud/Client/ClientRootViewController.swift b/ownCloud/Client/ClientRootViewController.swift index 7797ef54c..3ddd30e80 100644 --- a/ownCloud/Client/ClientRootViewController.swift +++ b/ownCloud/Client/ClientRootViewController.swift @@ -196,10 +196,10 @@ class ClientRootViewController: UITabBarController, BookmarkContainer, ToolAndTa core?.vault.keyValueStore?.storeObject(nil, forKey: .coreSkipAvailableOfflineKey) }, completionHandler: { (core, error) in if error == nil { - // Set up FP standby - if let core = core { - self.fpServiceStandby = OCFileProviderServiceStandby(core: core) - self.fpServiceStandby?.start() + // Start FP standby in 5 seconds regardless of connnection status + // (or below: after it's clear that authentication worked) + OnBackgroundQueue(async: true, after: 5.0) { [weak self] in + self?.startFPServiceStandbyIfNotRunning() } // Core is ready @@ -209,6 +209,13 @@ class ClientRootViewController: UITabBarController, BookmarkContainer, ToolAndTa OnMainThread { [weak self] () in self?.connectionStatusObservation = core?.observe(\OCCore.connectionStatus, options: [.initial], changeHandler: { [weak self] (_, _) in self?.updateConnectionStatusSummary() + + if let connectionStatus = self?.core?.connectionStatus, + connectionStatus == .online { + // Start FP service standby after it's clear that authentication worked + // (or above: after 5 seconds regardless of connnection status) + self?.startFPServiceStandbyIfNotRunning() + } }) } } else { @@ -224,6 +231,18 @@ class ClientRootViewController: UITabBarController, BookmarkContainer, ToolAndTa }) } + func startFPServiceStandbyIfNotRunning() { + // Set up FP standby + OCSynchronized(self) { + if let core = core, + core.state == .starting || core.state == .running, + self.fpServiceStandby == nil { + self.fpServiceStandby = OCFileProviderServiceStandby(core: core) + self.fpServiceStandby?.start() + } + } + } + var pushTransition : PushTransitionDelegate? override func viewDidLoad() { diff --git a/ownCloud/Release Notes/ReleaseNotes.plist b/ownCloud/Release Notes/ReleaseNotes.plist index 8877acac8..944f84cad 100644 --- a/ownCloud/Release Notes/ReleaseNotes.plist +++ b/ownCloud/Release Notes/ReleaseNotes.plist @@ -1502,6 +1502,53 @@ Added an optional "Wait for completion" option to the "Save File& + + Version + 11.9.0 + ReleaseNotes + + + Title + Biometrical Authentication Button + Subtitle + Added a biometrical authentication button to the passcode unlock view. The biomatrical cancel button enables passcode unlock as fallback. + Type + New + ImageName + lock.shield + + + Title + Rename Account + Subtitle + Renaming an account does no longer need a re-authentication + Type + Fix + ImageName + pencil + + + Title + Media Playback + Subtitle + Fixes a bug where media playback failed + Type + Fix + ImageName + wrench + + + Title + Faster Account Scan + Subtitle + We improved the time for the initial scan after setting up a new account + Type + Fix + ImageName + wrench + + + diff --git a/ownCloud/Resources/Assets.xcassets/Contents.json b/ownCloud/Resources/Assets.xcassets/Contents.json index da4a164c9..73c00596a 100644 --- a/ownCloud/Resources/Assets.xcassets/Contents.json +++ b/ownCloud/Resources/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/ownCloud/Resources/Assets.xcassets/biometrical-faceid.imageset/Contents.json b/ownCloud/Resources/Assets.xcassets/biometrical-faceid.imageset/Contents.json new file mode 100644 index 000000000..5791c7fb4 --- /dev/null +++ b/ownCloud/Resources/Assets.xcassets/biometrical-faceid.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "biometrical-faceid.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ownCloud/Resources/Assets.xcassets/biometrical-faceid.imageset/biometrical-faceid.pdf b/ownCloud/Resources/Assets.xcassets/biometrical-faceid.imageset/biometrical-faceid.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fc092104f1e32affceed81b674075e356a9082a9 GIT binary patch literal 4657 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 literal 0 HcmV?d00001 diff --git a/ownCloud/Resources/Assets.xcassets/biometrical-touchid.imageset/Contents.json b/ownCloud/Resources/Assets.xcassets/biometrical-touchid.imageset/Contents.json new file mode 100644 index 000000000..b7a7647cb --- /dev/null +++ b/ownCloud/Resources/Assets.xcassets/biometrical-touchid.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "filename" : "biometrical.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/ownCloud/Resources/Assets.xcassets/biometrical-touchid.imageset/biometrical.pdf b/ownCloud/Resources/Assets.xcassets/biometrical-touchid.imageset/biometrical.pdf new file mode 100644 index 0000000000000000000000000000000000000000..107c61243f11761a2ba186c77a3c58e1605817e9 GIT binary patch literal 5494 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 PfWhR!!opgH+TecyplWN^ literal 0 HcmV?d00001 diff --git a/ownCloud/Resources/ar.lproj/Localizable.strings b/ownCloud/Resources/ar.lproj/Localizable.strings index 1a986fbce61bcb0642a5e45780a27a9fc2086940..0bf4e5781b4f7577d965ae77ebfa92c3142ab7f3 100644 GIT binary patch delta 1734 zcmds2J4};N6h5`>A4OjBD%grY#g>Nx;sBcRs)DJYLhunMlTr#4N?U2MAOj984lW*J zjf07aqb4T*V2qOqabjR_Fb*anI+*wf8sq49{#shJ(MX&e{@#1f>pSP%@7ylFGkv>k zey~`yV}Pc}PZ9EB4N!XZ05(!e6d^7=oX^JLO!`?w%)R_VOCrfRh`>W9zguf3tgb>M};k{9=b$D(W z+0&PM*a7rmpoz5)d4zC^X85^j1oik)kz|z3R$K17Tg%EpN~J)KFplk z!nQfPHZ!9n^X z0Rj}`SC3rtVkMc)Vx7iLv_bf)Kjbm2DqzEUS7W+723Uz2#VM&zp^79fv0)(uk*-LJ zLYpWxW^|<=+91vDfysYT8R(4q+~9z)C!+0sTwTMfXG_?&QmKutJU6n_oo|Tpll#R+ zKMk|{l7+j*%L}B_(r@XSpC-ol%;at*wAXZg;YG1jKy29j;H9H=pvH#JqWv?;MTEJkcCd9&BE0E^O*-T5Jjo>Ohtr5QD@t**2Gy zO=F(SVI&l2$>s_a^+HnR2DHTmO@{)5C7TADAy8C{%^7Tl8<6eG-~z;Y3=q=|*>r%W zC{AXy6P?^3$inT)rVlh1q{n6Q$J?UJu53n=H?9$$oWLWt`A=04(`21G4k3`URDsG| zL0Z9%*qm4EWjFc2Mlr=?hD3%upy8zq#SE!Hx`ZKvArr_}V8{W|c?{W;<@Z?YmoVf5 zMGAl4vbh6goPl13sREgy1~$b7 z>`xOQ-w+u53Jj`1z9*1IGMxjc&=hE`;pC0`L^thIP-jwSo2*l$v^n7EE#~R7q!_t2 V|9U--ck{lV9n#yENHJbf1pxa&d20Xw diff --git a/ownCloud/Resources/de.lproj/Localizable.strings b/ownCloud/Resources/de.lproj/Localizable.strings index 12894c8a9b42093d8cde932d2ae7e0ce27f02470..db8e732dcb0a0254e8197fc32c8368b1b33c34f7 100644 GIT binary patch delta 1807 zcmaJ>U1%It6h1fEZMXT^CYjxA(xf^jWJ}Vxso*c6)`+ndqD{?~;+xa%cC+j+lkFxA z1tA#QJ{Smxb`*-57JLh&t`J4>p%f8(N{}K#MXMkrFXqLE64CG6+3jx8hGlkU&OPV5 z-}jw+=7Z0@_rCY8ECl%0wKhKF39BOidM(Iv2io~g_w(KqB`NQ;?omM}V5k89*&ir< z6!@Y3@g91U;#44;&QlMWbQo7ntd#07hWP7GJ9vA@S6Vn)=j9(4{LbwL)iQLXreKt^ z0GXmZz-;{DcsD77T?@GP(MxoMZ*Ceq_(wloukJjC_^gi4mTruH zRnPutlhH?MiUCJb&rlB7F}^zydJvyLJVoauUOEvSjRfOJeuXS-E=00eD$s0cc}{tp z?9<8@dc%$Ak_2+M=`=EJZhiL&{-V&dCx#UpTTSxjdnb*Q-m(C^6mXI{^KxGo+ZXq- zzxl<|y^HB%Cq#J;`qQ|ZG)YL#Ytf%eVU>O zZ><@f<9HTaaaT+mL{<#haqge*a(+IkcJX+`^H$Z@B?YDY0W4xI$9S$fSM}xu+3vrNuN^6G$1XTEKK8Vy1&sxX*y7qVOXo_zt@jRc_j1FPZQb8;d5l*sdET*5o5pU_z>%Ri26fVS8)zoI zj;ORbhc$6~09|UpHGtY zCgRGpVSSclppsgQYwr7(I{EfS`$*-KyXSurm2#F7AXJ@%9qIAnUBkH$R?qU_bH2tJ z-8^C&Ubj6r4+alLq0SvBQM4mP$2z5+x_T7fh5KRE_Dro)$Mwfd5Gtcnb*Hi>{Yf~v zu&qP++4S+4?K_~gfysmK z5}xniWSm8*l$Q;ESWR!Onpew?VBB%?^7v^r&Q^;u96PMq9kW&S{FgnxJg${Hb*%z! z(zxzw5jA=stBU@oW5X3y<*%>S=c;)m;6qCHVo)&(zvHaQ0Z#Bzv1@hXkc#yG3!bc{ AIRF3v delta 423 zcmaEKnsvq&)(w3kn`1dE8^-wU_+6N1sYKWv_gTQ3@B5?P!1%4j-7npLVWY1t1LQ` xZ7TUDn>0#IE|L&guE)g4GW~`bBhPjvPR1nu?HeQ*jpQazsNmSX#fDK&69B#1dvX8( diff --git a/ownCloud/Resources/el.lproj/InfoPlist.strings b/ownCloud/Resources/el.lproj/InfoPlist.strings index 8b36560ba7fca01d91a915a141c5ff52cbdea1a3..453295626b18cbff408afd56f816ae1de16c303a 100644 GIT binary patch delta 688 zcmZn@ye6^X9lNm)Lq07Lne@w$Kc42!%z<7S27d>*?9~p4B-qJ45>htClH4M zMN=mqWObb^z$$04mw6+z0>f73z06yg4>E6N-p_mhh<7rtV?GQNIReBA3`dx^1LYMM zHZmUtikxBI1JpCwfX&DCFp#?+C~*KNa1e+OGw%Xw+zZscpIMJViNTtI3rPAg1T)}q zBG4&@^=rV^fSj@gsCyUCtewo~z!n_=vO!jZ?AQsm?<~*;kmydJ_+}spk|)<2Ag?Jf z?3rxN=IV*hH~9R5EpP{gk7V%qX!0X=ugQGuY*~~BJuG!>1*V?O;AC(NmgfW48;rz3D_DiIYq}F06eP_H$nxqT`NKiMP0EK7@J2^at)#pC!ke3#n zI|CFXCyWt_!(`B${L$>Ut~AcJ%e#XEmKeI>7(R`ZI#S0di9{x4ks?JG@ZLhb)MJC# z#M3Gq>vy#12#*0Sg+y^%@Vb5L^~sOr@^YlArv=#Kz+z`EFomzIEv%0cU>*l|=Ci@q zT8{YJWZBgN*53IS<#>5xxvyO>lg2{8dbsd?z?VU68baw<1_Vg)V+rxcu75)OvC^8M3bH+wOiIjkn$*s3E|tnTNq3P7fnkU6!V z10-k7%8*$B=F$BXQ?a9gKggNn~AYSGCtrL$7%X2gNs^XeNYQ{C2^M4|GrqN}NmfIdb zT?Q>JfrRqDS%Nr&@-z^I$df$e@_0i|LUZ-`B%=SHM>EBH3w`-3Wys`N1fdYOk`K0( zlB`A3kS~H$2AahJN_^m1+dM$V(~XN!PsyCj;ygkA1z0}D38W#Pa8sMQ8Q=l#5SX%R zK2!pn5IS6t`UT`ZBIcWht*{4Ja7s@;8aMl2Hd}tQyL}!eBA&AqtB9$xZgSruFP(xu ztqthk2hms&>#%{sl5Do7{~bKji}ju)$UtxN);VA$2-49ZWRo~UvII15+5A!~tIziO z-*8Z_N29n{2{{g~ZieB~%<#WZk>u;?{+itYH*@N~p ymnM(XXYA}DOxUU)kL!-|nAyJUb@k7$oz?Fx&R}=-FyDa#__(4%T=e{;+?L-Gd!pnQ_ z+;e~D_dEBT^S=1jcJhkt(}!ITMOcQ#S(+JmPp}jlfRN(5i*>fr2REu!xLVQXJA|HT z=4U#48ZSSK;O8g{puS9kxQz{L~J=5d6SlrTsY+re5^h3rDANjA))>=3T5 z^MsMxnt8tcN2Mx>5K$zPlKcvs*VxTBI!6@f?owN-5H*H~BqoaZDSR7U6*tWKQ5Oo8 zoYwi8H@kK!@MTmLXSZ<`0_|I*+FyH)n7N*P0k9u?+>9NwD=_iq4RI&T6?5jT;}5{X zalhF(>vxM_gcsS;h`tE*A89bx;eg+UN}>DAIdl6Noj=)$QWHpEg+dak=xhjy#^h5P z>BnW#bhd~BGR!bv{@B=1X`tI8Zd;V@3Dl&s99{=;>p086(%B_A?J461@Y62Q)6AXF zwxF8Mb8~@5(g-<>2W|zMWP|vo$VW70l`AqkbJTkHL}N~Fj#N!aK?1w+S& z^u&h&uGMs)A&G$q?tbeS`d@UQX3`imJ1>8y!om5{jaKHPz`yYphu8o*P#RI6=-tF! zX7~JOy7+zrZ^*VPyiX~y+L5Y+4r~-&zo5aL!*+zE>o!G;lThr<*^+eKdQBTA{bCd4#s?Q{}vuYY|x`R%I-AMfMXfKU#5=8o|I- z8YPp6L9PEBs-nuvAga#xpq1*1I*jTpz(TADuK+&R;q?Mlnmi$$?P61s3C)@W?x0Gv z{QYL&p)H=O5yVS9-Zebl37G;oQL;@X{^y z9ydM#=cm*UBC*%tjWz$im0CMRU&!T+j=um9sr29g delta 709 zcmZ`$T}YEr7=BNFlVzJM_h)U>kFkqw<|q*w*uqAPE;b9Mg|(X)D`m^Y_N`!zek80+ zEqa3w>L%zH6%tQ0=`OmEq@=qfL~5{$A|l2JyOG|r1$h&PbKdv7=Xsy!Ip^H{qT-T$|ZAZl&#JszkqKO+kW9|5Tw->uYZoYhIP2wAolEj;% zLlS>?%rD{U30VN~bF9FZVn@Bke#pQ{7=bLDf&}FXWbyqCE6xU+9`;?6uy99fRQ`kc za9z@*saL`0dmSu`zjt+Rjle;OfDdG39Y%aG*r-v6KaT0J87ns}ngrw^gZK3T-a9eA z1)trrY|DZSr%5~u3NKH2ELeKv;$4qR48J{bqBLpXv8Uk%9Gz~(rRg6yHRI%!nFTM) zpzW<=M~?au#Gt?s3{x&cisA?)ar?)fhyrPRX1Xv85bJPFT{y}^C*=YYC-pNlHsSH`iuHhS zB~&;26JH?1Il^XYxav~riVJpTV#SJs z^)q9r_5vwVkJ3GagG6z#on3O^TwYt;A7qz34GB1puk&X0c9OZ&jU@A{Qi^%iu@u{< MPNbMm>VYQs1KY#Z8~^|S diff --git a/ownCloud/Resources/gl.lproj/Localizable.strings b/ownCloud/Resources/gl.lproj/Localizable.strings index 0e8ffee42cce5c22e151258663fa4b9b0ef57915..65b801cc00a46f15a0a5086fa7f79242aaa83252 100644 GIT binary patch delta 2053 zcmd5+T}V@57=Dj#Sz=3cbL#v#O@?5)RTLOYYWPQyO_$nL!J5B0cb5BWH%Y%pw_kL? zD}%bIo03CFka$;jS@%&;T_jO-A>9XN}9xjE`o_ z!Z%$a-dQTuLZ?(D&Q5Q`rbJw;H;ePtja6ebN@22-Of6V;^3pgYQLG8lZkrDqj4~+{ z6W=TLxO_AS%>*U0?0X>#LF$KGrUC3^>=lZWqAiurH;S_Unt~t%!z7C*?e+O>q=NdM%a>pow^da^j2OXs&S?T&9=V$>MgZ2+g?cwougChv{fL>Pw z?+mt8a_!T}_xa+IugK#-!~sNO;{alZu#055d1wd#2X%lsZAI-DtM=OWt*NyQJTMnB z^&x~G5VG^PVi&{{!%Fd$!(OYnd@rvwhe9k~U36?J1T3FxcdtkDI@`bm*KrORxief( z1Qo^rhW|3$Y-p+Zr|LU)hvA}NSW=-9G=iOsw;#>s;C@xw-&}j67nV`6^QY!YJF=IfODL#)n^=Vx} z2bh3dyYs_i5JL+l@iks4ZcUk`py)|d>{N7enX*9|7o96^s}rR3>XgcGnA9;MVFH$XB#$9I zcYgxDGIc@9y;k%CDpLcws1U0Gx+bhszq>C}J6+WCg^*(u9y}c>d;9u!dAOVW2Q7|F zksE|$OZAFLGK=m9g@<(3k&9<`1oiSf592Sj=N_#SO6}^Gtm<-|)GV4m7Hv_iy_0S* zlg*76Jn~H~gWJ@J$?%cZr7vB+znSmV-N=x#343+YBv~0u)B42lUBV@$)8eX9m+GX} zG!K98>h4+#``2+mW1N5!#*=})dbo0cR%cC;tu&V+jn1<^IvxXVd%6Ody7jS$18@3T UT#I;X!nwtVXxUucDVZC70+*A&_W%F@ delta 706 zcmZ`%JxCj27=BMLT}((coWd!P6Dc`tvyN6N>Mqr-ObMti2F zZ1TyYUeak4-(H%h0$r0Mes!LtlXGOEwD8$M(;}Nz`8?al#l>jM4S~2{CX+vBHI7cU zi-FRMV3)J;^W>5|i*MrPPUUD{guw&tZ%`BQ| zVu%Z(3kCB`TrQwHIQ zY?k6^TeUVf3m5EMu<8Mx3yieUG~wjIFO208tzZTGhEr*=J`_^4y zX)Gp{$xvQOW-}7Sfn7rgZh8?Z-Em{4N0EFbBf4fU^&?&%8U?Cg6cvLw$@q7P2#jlL z%(XJu4yw|A0JEMAne$=ShdpPn7usHYxpIs$Btmpnv6r$I%SZ7IW5-etY`OUY&RN>scQEl$ukSYsMY~gV>kAi=L^+0icxs_SsLP+d-b4GQ-W&BQgxiV2a z7Vl$Di4U#dR-bb#g7D)UfUPVpN2u__=tjddm2GO}n9H&lN-K!#w! zEfv?d=ogfsPsQ6%A5&EY6ZQRxzSuLPq4Yqqt7&?mponX#8yYoM znqvMFhJEB%KSwv0Gh%Z_L)u=ftH*ePYje?|J`ZM{cvhSI6SBmJ_98!=-*`hNvcFlW zUFfH5P`4$i1CMJJ0CpH-a}Z}9Jv`DAemTjt>rF}KdoZ?_ZzsO_da6LkkDp4M8kbEb q9ilX{e8{YGuUpDv+UF^YPL*8*d=kTPJEN_&+@-nwsZ%nXDfci@?9bz}KO6bJ;%b ze=&V%*c10fBiqa+XUnN0ll&lGG7uu z{CaM*YEq+XJ{>Byn)I7OZ^R}GdTGkg6qGH>=+BECQHPtXsLoY(Pz^5Q*<>?rt@I=Vv(9Y&mqC+VxO{{;p5qtuEw8k-8P$dLU}qL7C9yY~QZ$XV*`f6q&FO>{>TQ0~Er3hD~Ps<+HH-c9Y(3rYNEFsX-H{<@{#obUAE( O|5@tV-fer-(|ZNOPT}4F diff --git a/ownCloud/Resources/pt-BR.lproj/Localizable.strings b/ownCloud/Resources/pt-BR.lproj/Localizable.strings index e8d13f0c41c18749f768c42f283cc46b264b1d03..6211b91ccd14fdcecf48a5085382552e864d9fa7 100644 GIT binary patch delta 2651 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 bG%Lu%AQ!)mtz7;?e1K;S70rCAV3Yp8wSsAH delta 634 zcmYjOOG{f(5T4si(7Fhbn45}CB{6|o1? zpL)ACXr1PvuucoK2w%_&&Q)5L%`cf!oz@Vk;;&P=>)it2kk>8*i&B2c#Yr`TG zK{pwQ9><9($zLDC?JmSRJi>WM)r~mv{j-o154q@~RY4WRujYTXBYoF1>_mflSDG^o z2EFX0W=2f6?tsCTC!fQ&^@cM4@r@uSC};Z5V>B*?q+T~y?#Q=KgR;8ScMuz}I#l`Z XnXmDUt&2e8cgRZgHD08x4}<>zvt)vn diff --git a/ownCloud/Resources/sq.lproj/Localizable.strings b/ownCloud/Resources/sq.lproj/Localizable.strings index 079786a69c00f322c24fe6be95c2ab74f2c3e27e..ddc4dda183055ab7c710ccd59f84f5b5f6007b85 100644 GIT binary patch delta 1855 zcmah~O>7%g5T4iCt&&!8?3G!5>SP;X(xgq?pwdGlMBAhh6~v7LoKc!Mu@lF3HtU%B zKtL)54oDG$w3Gt{364~i_Rvc!^@dO?RrJY)=m9$zBDDNBNkv0^u_KpJXlnLuRLz%uusx)dd__{ityUs?F#E7s|myV_VnRUvH;vP zQYb_m3p`?D?mnzOiu{rEwZS zBz1o8e8t=Q*84l$?~kia@ABIdBZ)GUOQ=CXo2tTd31R183dZ@?(YV{0R_*aw&0>~P zGz);_mL;2`>&8yhmY-Q4UX*(qs^{>nU?-UufVLX<4!#x~l{DdwzIWgVc7V0eAtDd_ z$W0YUK}dmwNeK%1VK>O1uA3So8H>Z&knlRcBsescI{E`#XpaRcy_EqE%Pte4SwU^u1G5# zNQy5yiHL+EvM*@bfq=@lq|-Hq$^Y@XxhG z$|uaxq0Ygsl*1=GuPw>|O=x0-Hk-ey^_q3~l~M)cbq+hw$k#WAB0gy{>LRuI{^qzh z`S<0>6Vl2OQgdk2;A~!!xcb{3HIP`%10f?X!&wDVrsp&*>W;CYYoUw9tTZTQ3Fm%v zV>?zq_5)ZiLFRXeMW^UB?N7!v4-E;-$M-xOx623ett0VdGsyoiDM83e)%xn`D%Q7MEP_Ig)9;63Fp z-5>?D2})?j2_&|NCiAfvj% NLkEqkU41Io_b-oytAhXl delta 437 zcmbQThqY}f>xLJ?n~#a`u&^gGW>{~OGcckuIVeh_>{S2>s=gP{PdE{`Fdp%h{qP;UWHXDZNW z1%~9!R~r>s9I-0|Sp(6Y3shbVQVyhZzy>7(jmQK$B#R-DA#bvvne^m{HZ@jX1{Vh3 z$^C7moC-kMQlMPcX6E+EOw-qdGjdF>>EhYEs?*1rv1s!C{j!p!Ktof3#+CrxnF+Qa z2T12JWKWJgYi(GEc$HOLpmKvktc#{iu+Iq{(|Yc5b-?qul$vXd*$ ziB0}wDK>e^xd7!tU??a6)q=t!6KW^O=4^&aAejU*VRE8`=;o&LtF*=Qfr^0P4wMK3 kl6ed%Kr4V&ZI1nI&a%CZiLs7s#SnB<^pP;F|j+N1W*;Nj}k0sb^2j!*t><2Srp-MZ>B*_u?VY65!`R2lcYyW;FR zlnbR@84ZES`CRb5o$vdc3vwxy@+qEdFs4z65-FGLR0OI9N(NtA=*_FZE<`@c041Ah zO|E?GYDotrXll@?K*`UzwtQPWkCv}BYS$dM@|NEmt8~?``Vd{W+6i|uRYhZxi29^2^4^>#av&VX|z{;6qD5njV%ba8@k(O=$lP9l3!eE@obn1 zU01pEJS~FjMdUNK*KOALLY-qxCLCymaZ%I)SGuw5o1wB??Lg+*kbH4J8n1D2{h1j4 zcG$@|l>y$BP3&kq#Fi|dZiB&8WG$6EFq(~A7a_CH!38gsP&KiB+U=@9%4?AL$G(xN z=W=6!KWub5pOoaW#)ice;ID?7MNpVa6+$u^3Iu{-XwX^$ zJGX3F$zygali$H3CrVns7;t_haqHmz;J&Ru?*-yXr~o zF*``K^l@RAr*2SWVNbv<_z}x{Ak~LXh_rG&PCKDhdhmbs*FTrP#(_^ha7Aww-d>l+ zwPPvlyt$Meb#Bf%OGDS^X~bXL^m`+tWmbRj#4WGk8nwl6-VNg6ai?EbIVxoKfXt5Y zlqsi7szSkySH}l5KJ>n;Lm)99r7i&$GfW69z-k8+QfV1z`G9&ccIntvV;627UA=-T zomnmH`Q>_ zP0wq&Q$_+d z&EWEF9v;2z;MaeO<1Ke+-t?dihZ)a3cd|M3rQfi8wad=d&Bmiy)thB_8S3DfqSPp4b6eZT7429`e8Uh zZkMN_>5S3*W5B_GeC$YHizcu;L>i*$Vlb8KXXh4ZC(t>gWglR_Uqe*wv zi}K0}n>HMGxC4w$NRnJigHR$jLk*JCD^mb!byWwdbbXI`PXP3)P}%Y1;w&Fe-O?V|Qg>&UaE)e-2F>MzB7)p)RDkKg6MX;>y$*5&0g{xA4XnOIBL_Hf? zQDWxtiTO#<=u@#(%oUqpKx#hifvz@~m>7UZtvHeEZf3ksOIg{K8>Ic25WUaFQnWMv FzW~4PT2lZ3 delta 847 zcmZuwUr3Wt6u;j#w`KEW+qZA~w&lKQ%UCM9CT03C=cp;B2Chg7%2dk4NGFk{M)VLm zG&td2LZm0@p~kLYp!MKGRD=ySj0_|~B7E?{hvq{M9VZq+eBAq;KfiO&@0@e*+P$3b z>zeqCiMNSMYHYW5=>6aVGkAfoSi7~_0+Ou2o1B|=d}O!c@`-l-y840+X9D0Xg;Eee zPY?l$pd4)UDXf^enV)urpRb$H;Np^mEuprYbL<-H;`5=DnlCh#sjw_r$f3DG;Llse z)hMm+g! zc9q)LPf`#4d)ajyTByd^d80GJZv5l#BYZ#E23Q}v!aA`cZW-|trEOv|R8ToGX_cgV z$l{_^1QOIiE_vKkx{hYrNSI2}6}DsdQ)^C=xMGU*LjfG4vq~v`J0eKPLdv3} zh8-b%c5eCP*d1KoxY_#jh*zcLA)-ufXLcb|hum70l<7v_?A V!jyLIgDh<9`SV)lmWqT)$8QQs-!1?E diff --git a/ownCloud/Resources/zh_TW.lproj/Localizable.strings b/ownCloud/Resources/zh_TW.lproj/Localizable.strings index 1d5d4af2a6a553700c69591405e8e73bc090d726..745906f5410cb3d7ba523d9ccc5d725d35b87384 100644 GIT binary patch delta 3748 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 VU}|5`;N*gUV+#r5@IvRZ{{UBpq=Wzf diff --git a/ownCloud/Server List/ServerListTableViewController.swift b/ownCloud/Server List/ServerListTableViewController.swift index cef56d2b2..bb43aa2f9 100644 --- a/ownCloud/Server List/ServerListTableViewController.swift +++ b/ownCloud/Server List/ServerListTableViewController.swift @@ -722,7 +722,7 @@ class ServerListTableViewController: UITableViewController, Themeable, StateRest } if tableView.isEditing { - self.showBookmarkUI(edit: bookmark) + self.showBookmarkUI(edit: bookmark, removeAuthDataFromCopy: false) } else { self.connect(to: bookmark, lastVisibleItemId: nil, animated: true) self.tableView.deselectRow(at: indexPath, animated: true) @@ -756,7 +756,7 @@ class ServerListTableViewController: UITableViewController, Themeable, StateRest menuItems.append(openWindow) } let edit = UIAction(title: "Edit".localized, image: UIImage(systemName: "gear")) { _ in - self.showBookmarkUI(edit: bookmark) + self.showBookmarkUI(edit: bookmark, removeAuthDataFromCopy: false) } if VendorServices.shared.canEditAccount { menuItems.append(edit) @@ -853,7 +853,7 @@ class ServerListTableViewController: UITableViewController, Themeable, StateRest let editRowAction = UITableViewRowAction(style: .normal, title: "Edit".localized, handler: { [weak self] (_, indexPath) in if let bookmark = OCBookmarkManager.shared.bookmark(at: UInt(indexPath.row)) { - self?.showBookmarkUI(edit: bookmark) + self?.showBookmarkUI(edit: bookmark, removeAuthDataFromCopy: false) } }) editRowAction.backgroundColor = .blue diff --git a/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift b/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift index e9d4786c9..ec3f65406 100644 --- a/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift +++ b/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift @@ -50,6 +50,10 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { fatalError("init(coder:) has not been implemented") } + var askForUsernameFirst : Bool { + return OCServerLocator.useServerLocatorIdentifier != nil + } + override func viewDidLoad() { super.viewDidLoad() @@ -62,7 +66,11 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { self.addSection(onboardingSection()) } } else { - proceedWithLogin() + if askForUsernameFirst { + self.addSection(accountEntryMaskSection()) + } else { + proceedWithLogin() + } } } @@ -117,20 +125,53 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { return onboardingSection } + func accountEntryMaskSection() -> StaticTableViewSection { + var accountEntryMaskSection : StaticTableViewSection + + accountEntryMaskSection = StaticTableViewSection(headerTitle: nil, identifier: "accountEntryMaskSection") + accountEntryMaskSection.addStaticHeader(title: profile.welcome!, message: "Enter username".localized) + + accountEntryMaskSection.add(row: StaticTableViewRow(textFieldWithAction: { [weak self] (row, _, type) in + if type == .didBegin, let cell = row.cell, let indexPath = self?.tableView.indexPath(for: cell) { + self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) + } + if let value = row.value as? String { + self?.username = value + } + }, placeholder: "Username".localized, value: username ?? "", keyboardType: .asciiCapable, autocorrectionType: .no, autocapitalizationType: .none, returnKeyType: .continue, identifier: "username")) + + if VendorServices.shared.canAddAccount, OCBookmarkManager.shared.bookmarks.count > 0 { + let (proceedButton, cancelButton) = accountEntryMaskSection.addButtonFooter(proceedLabel: "Proceed".localized, proceedItemStyle: .welcome, cancelLabel: "Cancel".localized) + proceedButton?.addTarget(self, action: #selector(self.proceedWithLogin), for: .touchUpInside) + cancelButton?.addTarget(self, action: #selector(self.cancel(_:)), for: .touchUpInside) + } else { + let (proceedButton, _) = accountEntryMaskSection.addButtonFooter(proceedLabel: "Proceed".localized, proceedItemStyle: .welcome, cancelLabel: nil) + proceedButton?.addTarget(self, action: #selector(self.proceedWithLogin), for: .touchUpInside) + } + + return accountEntryMaskSection + } + func loginMaskSection() -> StaticTableViewSection { var loginMaskSection : StaticTableViewSection loginMaskSection = StaticTableViewSection(headerTitle: nil, identifier: "loginMaskSection") loginMaskSection.addStaticHeader(title: profile.welcome!, message: profile.promptForPasswordAuth) - loginMaskSection.add(row: StaticTableViewRow(textFieldWithAction: { [weak self] (row, _, type) in + let userNameRow = StaticTableViewRow(textFieldWithAction: { [weak self] (row, _, type) in if type == .didBegin, let cell = row.cell, let indexPath = self?.tableView.indexPath(for: cell) { self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) } if let value = row.value as? String { self?.username = value } - }, placeholder: "Username".localized, keyboardType: .asciiCapable, autocorrectionType: .no, autocapitalizationType: .none, returnKeyType: .continue, identifier: "username", borderStyle: .roundedRect)) + }, placeholder: "Username".localized, value: self.username ?? "", keyboardType: .asciiCapable, autocorrectionType: .no, autocapitalizationType: .none, returnKeyType: .continue, identifier: "username", borderStyle: .roundedRect) + + if let username = self.username, username.count > 0 { + userNameRow.enabled = false + } + + loginMaskSection.add(row: userNameRow) passwordRow = StaticTableViewRow(secureTextFieldWithAction: { [weak self] (row, _, type) in if type == .didBegin, let cell = row.cell, let indexPath = self?.tableView.indexPath(for: cell) { @@ -320,6 +361,9 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { return } + if let accountEntryMaskSection = self.sectionForIdentifier("accountEntryMaskSection") { + self.removeSection(accountEntryMaskSection) + } if let urlSection = self.sectionForIdentifier("urlSection") { self.removeSection(urlSection) } @@ -352,6 +396,8 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { if OCAuthenticationMethod.registeredAuthenticationMethod(forIdentifier: authMethodIdentifier)?.type == .passphrase { options[.usernameKey] = username ?? "" options[.passphraseKey] = password ?? "" + } else if askForUsernameFirst, let username = username { + options[.usernameKey] = username } options[.presentingViewControllerKey] = self @@ -467,7 +513,9 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { guard let bookmark = self.bookmark else { return } let connection = instantiateConnection(for: bookmark) - connection.prepareForSetup(options: nil, completionHandler: { (connectionIssue, _, _, preferredAuthenticationMethods) in + connection.prepareForSetup(options: ((username != nil) ? [ + .userName : username! + ] : nil), completionHandler: { (connectionIssue, _, _, preferredAuthenticationMethods) in var proceed : Bool = true if let issue = connectionIssue { @@ -499,7 +547,11 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { self.addSection(self.onboardingSection()) } } else { - self.cancel(nil) + if self.askForUsernameFirst { + self.addSection(self.accountEntryMaskSection()) + } else { + self.cancel(nil) + } } } }) @@ -545,6 +597,10 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { if self.sectionForIdentifier("tokenMaskSection") == nil { self.addSection(self.tokenMaskSection()) } + + if self.username != nil { + self.startAuthentication(nil) + } } if self.profile.isOnboardingEnabled, self.sectionForIdentifier("onboardingSection") == nil { diff --git a/ownCloud/Static Login/Interface/StaticLoginSingleAccountServerListViewController.swift b/ownCloud/Static Login/Interface/StaticLoginSingleAccountServerListViewController.swift index 3981767fd..4ff062240 100644 --- a/ownCloud/Static Login/Interface/StaticLoginSingleAccountServerListViewController.swift +++ b/ownCloud/Static Login/Interface/StaticLoginSingleAccountServerListViewController.swift @@ -284,7 +284,7 @@ class StaticLoginSingleAccountServerListViewController: ServerListTableViewContr tableView.deselectRow(at: indexPath, animated: true) switch actionRows[indexPath.row] { case .editLogin: - showBookmarkUI(edit: bookmark) + showBookmarkUI(edit: bookmark, removeAuthDataFromCopy: false) case .manageStorage: showBookmarkInfoUI(bookmark) diff --git a/ownCloud/Static Login/Interface/StaticLoginViewController.swift b/ownCloud/Static Login/Interface/StaticLoginViewController.swift index a88762eb7..6a0ba50c4 100644 --- a/ownCloud/Static Login/Interface/StaticLoginViewController.swift +++ b/ownCloud/Static Login/Interface/StaticLoginViewController.swift @@ -80,7 +80,7 @@ class StaticLoginViewController: UIViewController, Themeable, StateRestorationCo var toolbarShown : Bool = false { didSet { - if self.toolbarItems == nil, toolbarShown { + if self.toolbarItems == nil, toolbarShown, OCBookmarkManager.shared.bookmarks.count == 0 { let settingsBarButtonItem = UIBarButtonItem(title: "Settings".localized, style: UIBarButtonItem.Style.plain, target: self, action: #selector(settings)) settingsBarButtonItem.accessibilityIdentifier = "settingsBarButtonItem" @@ -248,6 +248,7 @@ class StaticLoginViewController: UIViewController, Themeable, StateRestorationCo firstViewController = self.buildBookmarkSelector() } else if let firstProfile = loginBundle.profiles.first { // Setup flow + self.toolbarShown = true if loginBundle.profiles.count > 1 { // Profile setup selector firstViewController = buildProfileSetupSelector(title: firstProfile.welcome!) diff --git a/ownCloudAppShared/AppLock/AppLockManager.swift b/ownCloudAppShared/AppLock/AppLockManager.swift index 19736b4dc..1b2f00990 100644 --- a/ownCloudAppShared/AppLock/AppLockManager.swift +++ b/ownCloudAppShared/AppLock/AppLockManager.swift @@ -80,6 +80,14 @@ public class AppLockManager: NSObject { self.userDefaults.set(newValue, forKey: "applock-locked-until-date") } } + private var biometricalAuthenticationSucceeded: Bool { + get { + return userDefaults.bool(forKey: "applock-biometrical-authentication-succeeded") + } + set(newValue) { + self.userDefaults.set(newValue, forKey: "applock-biometrical-authentication-succeeded") + } + } private let maximumPasscodeAttempts: Int = 3 private let powBaseDelay: Double = 1.5 @@ -146,7 +154,7 @@ public class AppLockManager: NSObject { lockscreenOpen = true // Show biometrical - if !forceShow, !self.shouldDisplayCountdown { + if !forceShow, !self.shouldDisplayCountdown, self.biometricalAuthenticationSucceeded { showBiometricalAuthenticationInterface(context: context) } } @@ -187,6 +195,8 @@ public class AppLockManager: NSObject { private var passcodeControllerByWindow : NSMapTable = NSMapTable.weakToStrongObjects() private var applockWindowByWindow : NSMapTable = NSMapTable.weakToStrongObjects() + open var biometricCancelLabel : String? + open var cancelAction : (() -> Void)? open var successAction : (() -> Void)? @@ -282,7 +292,12 @@ public class AppLockManager: NSObject { func passwordViewController() -> PasscodeViewController { var passcodeViewController : PasscodeViewController - passcodeViewController = PasscodeViewController(completionHandler: { (viewController: PasscodeViewController, passcode: String) in + passcodeViewController = PasscodeViewController(biometricalHandler: { (passcodeViewController) in + if !self.shouldDisplayCountdown { + let context = LAContext() + self.showBiometricalAuthenticationInterface(context: context) + } + }, completionHandler: { (viewController: PasscodeViewController, passcode: String) in self.attemptUnlock(with: passcode, passcodeViewController: viewController) }, requiredLength: AppLockManager.shared.passcode?.count ?? AppLockSettings.shared.requiredPasscodeDigits) @@ -452,7 +467,7 @@ public class AppLockManager: NSObject { } } - context.localizedCancelTitle = "Enter code".localized + context.localizedCancelTitle = biometricCancelLabel ?? "Enter code".localized context.localizedFallbackTitle = "" self.biometricalAuthenticationInterfaceShown = true @@ -461,6 +476,7 @@ public class AppLockManager: NSObject { self.biometricalAuthenticationInterfaceShown = false if success { + self.biometricalAuthenticationSucceeded = true // Fill the passcode dots OnMainThread { self.performPasscodeViewControllerUpdates { (passcodeViewController) in @@ -478,6 +494,7 @@ public class AppLockManager: NSObject { self.attemptUnlock(with: self.passcode) } } else { + self.biometricalAuthenticationSucceeded = false if let error = error { switch error { case LAError.biometryLockout: diff --git a/ownCloudAppShared/AppLock/PasscodeViewController.swift b/ownCloudAppShared/AppLock/PasscodeViewController.swift index 7fb5ae2b1..de640f6ac 100644 --- a/ownCloudAppShared/AppLock/PasscodeViewController.swift +++ b/ownCloudAppShared/AppLock/PasscodeViewController.swift @@ -17,8 +17,11 @@ */ import UIKit +import ownCloudApp +import LocalAuthentication public typealias PasscodeViewControllerCancelHandler = ((_ passcodeViewController: PasscodeViewController) -> Void) +public typealias PasscodeViewControllerBiometricalHandler = ((_ passcodeViewController: PasscodeViewController) -> Void) public typealias PasscodeViewControllerCompletionHandler = ((_ passcodeViewController: PasscodeViewController, _ passcode: String) -> Void) public class PasscodeViewController: UIViewController, Themeable { @@ -39,6 +42,8 @@ public class PasscodeViewController: UIViewController, Themeable { @IBOutlet private var keypadButtons: [ThemeRoundedButton]? @IBOutlet private var deleteButton: ThemeButton? @IBOutlet public var cancelButton: ThemeButton? + @IBOutlet public var biometricalButton: ThemeButton? + @IBOutlet public var biometricalImageView: UIImageView? @IBOutlet public var compactHeightPasscodeTextField: UITextField? // MARK: - Properties @@ -109,6 +114,15 @@ public class PasscodeViewController: UIViewController, Themeable { } } + var biometricalButtonHidden: Bool = false { + didSet { + biometricalButton?.isEnabled = biometricalButtonHidden + biometricalButton?.isHidden = !biometricalButtonHidden + biometricalImageView?.isHidden = !biometricalButtonHidden + biometricalImageView?.image = LAContext().biometricsAuthenticationImage() + } + } + var hasCompactHeight: Bool { if self.traitCollection.verticalSizeClass == .compact { return true @@ -119,11 +133,13 @@ public class PasscodeViewController: UIViewController, Themeable { // MARK: - Handlers public var cancelHandler: PasscodeViewControllerCancelHandler? + public var biometricalHandler: PasscodeViewControllerBiometricalHandler? public var completionHandler: PasscodeViewControllerCompletionHandler? // MARK: - Init - public init(cancelHandler: PasscodeViewControllerCancelHandler? = nil, completionHandler: @escaping PasscodeViewControllerCompletionHandler, hasCancelButton: Bool = true, keypadButtonsEnabled: Bool = true, requiredLength: Int) { + public init(cancelHandler: PasscodeViewControllerCancelHandler? = nil, biometricalHandler: PasscodeViewControllerBiometricalHandler? = nil, completionHandler: @escaping PasscodeViewControllerCompletionHandler, hasCancelButton: Bool = true, keypadButtonsEnabled: Bool = true, requiredLength: Int) { self.cancelHandler = cancelHandler + self.biometricalHandler = biometricalHandler self.completionHandler = completionHandler self.keypadButtonsEnabled = keypadButtonsEnabled self.cancelButtonHidden = hasCancelButton @@ -157,7 +173,11 @@ public class PasscodeViewController: UIViewController, Themeable { self.screenBlurringEnabled = { self.screenBlurringEnabled }() self.errorMessageLabel?.minimumScaleFactor = 0.5 self.errorMessageLabel?.adjustsFontSizeToFitWidth = true + self.biometricalButtonHidden = !(!AppLockSettings.shared.biometricalSecurityEnabled || self.cancelButtonHidden) updateKeypadButtons() + if let biometricalSecurityName = LAContext().supportedBiometricsAuthenticationName() { + self.biometricalButton?.accessibilityLabel = biometricalSecurityName + } if #available(iOS 13.4, *) { for button in keypadButtons! { @@ -165,6 +185,7 @@ public class PasscodeViewController: UIViewController, Themeable { } PointerEffect.install(on: cancelButton!, effectStyle: .highlight) PointerEffect.install(on: deleteButton!, effectStyle: .highlight) + PointerEffect.install(on: biometricalButton!, effectStyle: .highlight) } } @@ -283,6 +304,10 @@ public class PasscodeViewController: UIViewController, Themeable { cancelHandler?(self) } + @IBAction func biometricalAction(_ sender: UIButton) { + biometricalHandler?(self) + } + // MARK: - Themeing public override var preferredStatusBarStyle : UIStatusBarStyle { if VendorServices.shared.isBranded { @@ -311,6 +336,8 @@ public class PasscodeViewController: UIViewController, Themeable { deleteButton?.themeColorCollection = ThemeColorPairCollection(fromPair: ThemeColorPair(foreground: collection.neutralColors.normal.background, background: .clear)) + biometricalImageView?.tintColor = collection.tintColor + cancelButton?.applyThemeCollection(collection, itemStyle: .defaultForItem) } } diff --git a/ownCloudAppShared/AppLock/PasscodeViewController.xib b/ownCloudAppShared/AppLock/PasscodeViewController.xib index 4878a3b6b..3f25d1817 100644 --- a/ownCloudAppShared/AppLock/PasscodeViewController.xib +++ b/ownCloudAppShared/AppLock/PasscodeViewController.xib @@ -1,9 +1,9 @@ - + - + @@ -11,6 +11,8 @@ + + @@ -199,6 +201,21 @@ + + + + + + + + + + + @@ -348,6 +372,9 @@ + + + @@ -370,4 +397,7 @@ + + + diff --git a/ownCloudAppShared/UIKit Extension/LAContext+Extension.swift b/ownCloudAppShared/UIKit Extension/LAContext+Extension.swift index 8c620920e..8233889af 100644 --- a/ownCloudAppShared/UIKit Extension/LAContext+Extension.swift +++ b/ownCloudAppShared/UIKit Extension/LAContext+Extension.swift @@ -17,11 +17,11 @@ */ import LocalAuthentication +import UIKit extension LAContext { public func supportedBiometricsAuthenticationName() -> String? { - if canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) { switch self.biometryType { case .faceID : return "Face ID".localized @@ -30,5 +30,24 @@ extension LAContext { } } return nil - } + } + + public func biometricsAuthenticationImage() -> UIImage? { + if canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) { + switch self.biometryType { + case .faceID : if #available(iOSApplicationExtension 13.0, *) { + return UIImage(systemName: "faceid") + } else { + return UIImage(named: "biometrical-faceid") + } + case .touchID: if #available(iOSApplicationExtension 13.0, *) { + return UIImage(systemName: "touchid") + } else { + return UIImage(named: "biometrical-touchid") + } + case .none: return nil + } + } + return nil + } } diff --git a/ownCloudAppShared/User Interface/Progress/ProgressIndicatorViewController.swift b/ownCloudAppShared/User Interface/Progress/ProgressIndicatorViewController.swift index a7196258d..1ebe6c794 100644 --- a/ownCloudAppShared/User Interface/Progress/ProgressIndicatorViewController.swift +++ b/ownCloudAppShared/User Interface/Progress/ProgressIndicatorViewController.swift @@ -1,5 +1,5 @@ // -// FullProgressViewController.swift +// ProgressIndicatorViewController.swift // ownCloud Share Extension // // Created by Felix Schwarz on 07.08.20. @@ -23,22 +23,81 @@ open class ProgressIndicatorViewController: UIViewController, Themeable { open var cancelHandler : (() -> Void)? open var progressView : UIProgressView + open var activityIndicator : UIActivityIndicatorView open var label : UILabel + open var titleLabel : UILabel? open var cancelButton : UIButton? - public init(initialProgressLabel: String?, cancelLabel: String? = nil, cancelHandler: (() -> Void)? = nil) { + var progressMessageObservation : NSKeyValueObservation? + var progressValueObservation : NSKeyValueObservation? + open var progress : Progress? { + willSet { + progressMessageObservation?.invalidate() + progressMessageObservation = nil + + progressValueObservation?.invalidate() + progressValueObservation = nil + } + + didSet { + if progress != nil { + progressMessageObservation = progress?.observe(\Progress.localizedDescription, options: NSKeyValueObservingOptions.initial, changeHandler: { [weak self] progress, _ in + OnMainThread { + self?.label.text = progress.localizedDescription + } + }) + + progressValueObservation = progress?.observe(\Progress.fractionCompleted, options: NSKeyValueObservingOptions.initial, changeHandler: { [weak self] progress, _ in + OnMainThread { + if progress.isIndeterminate { + self?.activityIndicator.isHidden = false + self?.progressView.isHidden = true + self?.activityIndicator.startAnimating() + } else { + self?.activityIndicator.stopAnimating() + self?.activityIndicator.isHidden = true + self?.progressView.isHidden = false + } + } + }) + } + } + } + + public init(initialTitleLabel: String? = nil, initialProgressLabel: String?, progress : Progress?, cancelLabel: String? = nil, cancelHandler: (() -> Void)? = nil) { progressView = UIProgressView(progressViewStyle: .bar) progressView.translatesAutoresizingMaskIntoConstraints = false + if #available(iOS 13, *) { + activityIndicator = UIActivityIndicatorView(style: .large) + } else { + activityIndicator = UIActivityIndicatorView(style: .whiteLarge) + } + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + activityIndicator.isHidden = true + label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false super.init(nibName: nil, bundle: nil) + self.progress = progress + + if let initialTitleLabel = initialTitleLabel { + titleLabel = UILabel() + titleLabel?.translatesAutoresizingMaskIntoConstraints = false + titleLabel?.text = initialTitleLabel + titleLabel?.font = .preferredFont(forTextStyle: .title2) + + label.font = .preferredFont(forTextStyle: .subheadline) + } + if let initialProgressLabel = initialProgressLabel { label.text = initialProgressLabel } + self.cancelHandler = cancelHandler + if cancelHandler != nil { cancelButton = ThemeButton(type: .system) cancelButton?.translatesAutoresizingMaskIntoConstraints = false @@ -63,14 +122,20 @@ open class ProgressIndicatorViewController: UIViewController, Themeable { centerView.addSubview(progressView) centerView.addSubview(label) + if let titleLabel = titleLabel { + centerView.addSubview(titleLabel) + } + if let cancelButton = cancelButton { centerView.addSubview(cancelButton) } rootView.addSubview(centerView) + rootView.addSubview(activityIndicator) let outerSpacing : CGFloat = 10 - let labelProgressBarSpacing : CGFloat = 15 + let titleLabelSpacing : CGFloat = 5 + let labelProgressBarSpacing : CGFloat = 20 let cancelProgressBarSpacing : CGFloat = 40 let progressBarWidth : CGFloat = 280 @@ -83,7 +148,6 @@ open class ProgressIndicatorViewController: UIViewController, Themeable { label.centerXAnchor.constraint(equalTo: centerView.centerXAnchor), - label.topAnchor.constraint(equalTo: centerView.topAnchor, constant: outerSpacing), progressView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: labelProgressBarSpacing), centerView.leftAnchor.constraint(greaterThanOrEqualTo: rootView.leftAnchor, constant: outerSpacing), @@ -92,9 +156,28 @@ open class ProgressIndicatorViewController: UIViewController, Themeable { centerView.centerXAnchor.constraint(equalTo: rootView.centerXAnchor), centerView.centerYAnchor.constraint(equalTo: rootView.centerYAnchor), - centerView.widthAnchor.constraint(equalToConstant: progressBarWidth) + centerView.widthAnchor.constraint(equalToConstant: progressBarWidth), + + activityIndicator.centerXAnchor.constraint(equalTo: centerView.centerXAnchor), + activityIndicator.bottomAnchor.constraint(equalTo: centerView.topAnchor, constant:-labelProgressBarSpacing), + activityIndicator.widthAnchor.constraint(equalToConstant: 20), + activityIndicator.heightAnchor.constraint(equalToConstant: 20) ] + if let titleLabel = titleLabel { + constraints.append(contentsOf: [ + titleLabel.topAnchor.constraint(equalTo: centerView.topAnchor, constant: outerSpacing), + titleLabel.centerXAnchor.constraint(greaterThanOrEqualTo: centerView.centerXAnchor), + titleLabel.leftAnchor.constraint(greaterThanOrEqualTo: centerView.leftAnchor), + titleLabel.rightAnchor.constraint(lessThanOrEqualTo: centerView.rightAnchor), + label.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: titleLabelSpacing) + ]) + } else { + constraints.append(contentsOf: [ + label.topAnchor.constraint(equalTo: centerView.topAnchor, constant: outerSpacing), + ]) + } + if let cancelButton = cancelButton { constraints.append(contentsOf: [ cancelButton.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: cancelProgressBarSpacing), @@ -132,8 +215,10 @@ open class ProgressIndicatorViewController: UIViewController, Themeable { self.view.backgroundColor = collection.tableBackgroundColor self.progressView.applyThemeCollection(collection) + self.titleLabel?.applyThemeCollection(collection, itemStyle: .title, itemState: .normal) self.label.applyThemeCollection(collection) self.cancelButton?.applyThemeCollection(collection) + self.activityIndicator.style = collection.activityIndicatorViewStyle } open func update(progress: Float? = nil, text: String? = nil) { diff --git a/ownCloudAppShared/User Interface/Theme/UI/ThemeTableViewCell.swift b/ownCloudAppShared/User Interface/Theme/UI/ThemeTableViewCell.swift index 12e8a8831..919f8c732 100644 --- a/ownCloudAppShared/User Interface/Theme/UI/ThemeTableViewCell.swift +++ b/ownCloudAppShared/User Interface/Theme/UI/ThemeTableViewCell.swift @@ -145,6 +145,10 @@ open class ThemeTableViewCell: UITableViewCell, Themeable { textColor = collection.tableRowColors.labelColor backgroundColor = collection.tableRowColors.backgroundColor + case .text: + textColor = collection.tableRowColors.labelColor + backgroundColor = collection.tableRowColors.backgroundColor + case .confirmation: textColor = collection.approvalColors.normal.foreground backgroundColor = collection.approvalColors.normal.background