diff --git a/.xcode-version b/.xcode-version
index c3d10c59d..c27905ac3 100644
--- a/.xcode-version
+++ b/.xcode-version
@@ -1 +1 @@
-13.3.1
+13.4.1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1bbe8ef1b..e06254fa1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,41 @@
+Changelog for ownCloud iOS Client [11.10.1] (2022-08-02)
+=======================================
+The following sections list the changes in ownCloud iOS Client 11.10.1 relevant to
+ownCloud admins and users.
+
+[11.10.1]: https://github.com/owncloud/ios-app/compare/milestone/11.10.0...milestone/11.10.1
+
+Summary
+-------
+
+* Bugfix - (Branding) Biometrical Unlock in Share Sheet: [#1129](https://github.com/owncloud/ios-app/pull/1129)
+* Bugfix - Show folder contents from cache when offline: [#1130](https://github.com/owncloud/ios-app/issues/1130)
+* Bugfix - (Branding) Color Issues: [#1132](https://github.com/owncloud/ios-app/pull/1132)
+
+Details
+-------
+
+* Bugfix - (Branding) Biometrical Unlock in Share Sheet: [#1129](https://github.com/owncloud/ios-app/pull/1129)
+
+ Biometrical unlock in the share sheet does not work in some third party apps like Boxer. With new
+ branding parameters it is now possible to disable biometrical unlock in the share sheet or to
+ exclude specific apps.
+
+ https://github.com/owncloud/ios-app/pull/1129
+
+* Bugfix - Show folder contents from cache when offline: [#1130](https://github.com/owncloud/ios-app/issues/1130)
+
+ With this fix the app shows the contents of the available folders when offline.
+
+ https://github.com/owncloud/ios-app/issues/1130
+
+* Bugfix - (Branding) Color Issues: [#1132](https://github.com/owncloud/ios-app/pull/1132)
+
+ Fix some automatic color values, if the branding color is bright by checking the brightness of
+ the color.
+
+ https://github.com/owncloud/ios-app/pull/1132
+
Changelog for ownCloud iOS Client [11.10.0] (2022-05-18)
=======================================
The following sections list the changes in ownCloud iOS Client 11.10.0 relevant to
diff --git a/changelog/11.10.1_2022-08-02/1129 b/changelog/11.10.1_2022-08-02/1129
new file mode 100644
index 000000000..64f0996d0
--- /dev/null
+++ b/changelog/11.10.1_2022-08-02/1129
@@ -0,0 +1,5 @@
+Bugfix: (Branding) Biometrical Unlock in Share Sheet
+
+Biometrical unlock in the share sheet does not work in some third party apps like Boxer. With new branding parameters it is now possible to disable biometrical unlock in the share sheet or to exclude specific apps.
+
+https://github.com/owncloud/ios-app/pull/1129
diff --git a/changelog/11.10.1_2022-08-02/1130 b/changelog/11.10.1_2022-08-02/1130
new file mode 100644
index 000000000..630ff061d
--- /dev/null
+++ b/changelog/11.10.1_2022-08-02/1130
@@ -0,0 +1,5 @@
+Bugfix: Show folder contents from cache when offline
+
+With this fix the app shows the contents of the available folders when offline.
+
+https://github.com/owncloud/ios-app/issues/1130
diff --git a/changelog/11.10.1_2022-08-02/1132 b/changelog/11.10.1_2022-08-02/1132
new file mode 100644
index 000000000..3275f1a31
--- /dev/null
+++ b/changelog/11.10.1_2022-08-02/1132
@@ -0,0 +1,5 @@
+Bugfix: (Branding) Color Issues
+
+Fix some automatic color values, if the branding color is bright by checking the brightness of the color.
+
+https://github.com/owncloud/ios-app/pull/1132
diff --git a/fastlane/metadata-emm/en-US/release_notes.txt b/fastlane/metadata-emm/en-US/release_notes.txt
index 5da11c0dc..ffd45f70d 100644
--- a/fastlane/metadata-emm/en-US/release_notes.txt
+++ b/fastlane/metadata-emm/en-US/release_notes.txt
@@ -1,9 +1,3 @@
-• UI fixes on iOS 15
-This version fixes some UI problems on iOS 15.
-
-• Shortcuts Action
-The shortcuts action Delete Path Item did not provided configured accounts.
-
-• Increased Timeout for Copy Action
-Timeout for Copy Action was increased up to 10 minutes.
+• Available Offline Folders
+Shows the contents of the available folders when offline.
diff --git a/fastlane/metadata-owncloud-online/en-US/release_notes.txt b/fastlane/metadata-owncloud-online/en-US/release_notes.txt
index 5da11c0dc..ffd45f70d 100644
--- a/fastlane/metadata-owncloud-online/en-US/release_notes.txt
+++ b/fastlane/metadata-owncloud-online/en-US/release_notes.txt
@@ -1,9 +1,3 @@
-• UI fixes on iOS 15
-This version fixes some UI problems on iOS 15.
-
-• Shortcuts Action
-The shortcuts action Delete Path Item did not provided configured accounts.
-
-• Increased Timeout for Copy Action
-Timeout for Copy Action was increased up to 10 minutes.
+• Available Offline Folders
+Shows the contents of the available folders when offline.
diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt
index 5da11c0dc..ffd45f70d 100644
--- a/fastlane/metadata/en-US/release_notes.txt
+++ b/fastlane/metadata/en-US/release_notes.txt
@@ -1,9 +1,3 @@
-• UI fixes on iOS 15
-This version fixes some UI problems on iOS 15.
-
-• Shortcuts Action
-The shortcuts action Delete Path Item did not provided configured accounts.
-
-• Increased Timeout for Copy Action
-Timeout for Copy Action was increased up to 10 minutes.
+• Available Offline Folders
+Shows the contents of the available folders when offline.
diff --git a/ios-sdk b/ios-sdk
index 6bcd960e1..57729a606 160000
--- a/ios-sdk
+++ b/ios-sdk
@@ -1 +1 @@
-Subproject commit 6bcd960e12f6beddddd0afea5027fd11281afc72
+Subproject commit 57729a60605cfc810c7e858d1875fc49006ec7f9
diff --git a/ownCloud Share Extension/Info.plist b/ownCloud Share Extension/Info.plist
index d06c3a6ff..8b1fc44c7 100644
--- a/ownCloud Share Extension/Info.plist
+++ b/ownCloud Share Extension/Info.plist
@@ -46,6 +46,8 @@
group.com.owncloud.ios-app
OCAppIdentifierPrefix
$(AppIdentifierPrefix)
+ OCAppComponentIdentifier
+ shareExtension
OCHasFileProvider
OCKeychainAccessGroupIdentifier
diff --git a/ownCloud Share Extension/ShareNavigationController.swift b/ownCloud Share Extension/ShareNavigationController.swift
index 84d5e9fd2..42db1e411 100644
--- a/ownCloud Share Extension/ShareNavigationController.swift
+++ b/ownCloud Share Extension/ShareNavigationController.swift
@@ -35,6 +35,14 @@ class ShareNavigationController: AppExtensionNavigationController {
self.setViewControllers([viewController], animated: false)
}
}
+
+ override func willMove(toParent parent: UIViewController?) {
+ super.willMove(toParent: parent)
+
+ OCAppIdentity.shared.hostAppBundleIdentifier = parent?.oc_hostAppBundleIdentifier
+
+ Log.debug("Extension Host App Bundle ID: \(OCAppIdentity.shared.hostAppBundleIdentifier ?? "nil")")
+ }
}
extension UserInterfaceContext : UserInterfaceContextProvider {
diff --git a/ownCloud.xcodeproj/project.pbxproj b/ownCloud.xcodeproj/project.pbxproj
index 496ac4819..72b9ed457 100644
--- a/ownCloud.xcodeproj/project.pbxproj
+++ b/ownCloud.xcodeproj/project.pbxproj
@@ -311,6 +311,8 @@
DC3BE0D82077BC5D002A0AC0 /* ownCloudSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 239369782076110900BCE21A /* ownCloudSDK.framework */; };
DC3BE0DA2077BC6B002A0AC0 /* ownCloudSDK.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 239369782076110900BCE21A /* ownCloudSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DC3BE0DF2077CC14002A0AC0 /* ClientRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3BE0DD2077CC13002A0AC0 /* ClientRootViewController.swift */; };
+ DC3DDF06287E1C0800E5586D /* UIViewController+HostBundleID.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3DDF03287E1AC200E5586D /* UIViewController+HostBundleID.m */; };
+ DC3DDF07287E1C0E00E5586D /* UIViewController+HostBundleID.h in Headers */ = {isa = PBXBuildFile; fileRef = DC3DDF02287E1AC200E5586D /* UIViewController+HostBundleID.h */; settings = {ATTRIBUTES = (Public, ); }; };
DC3DEC7B22AFA1F000F3352D /* DownloadItemsHUDViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3DEC7A22AFA1F000F3352D /* DownloadItemsHUDViewController.swift */; };
DC3F4522271A23A000ED2383 /* AcknowledgementsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3F4521271A23A000ED2383 /* AcknowledgementsTableViewController.swift */; };
DC4332002472E1B4002DC0E5 /* OCLicenseEMMProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = DC4331FE2472E1B4002DC0E5 /* OCLicenseEMMProvider.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1239,6 +1241,8 @@
DC3BE0DC2077CC13002A0AC0 /* ClientQueryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientQueryViewController.swift; sourceTree = ""; };
DC3BE0DD2077CC13002A0AC0 /* ClientRootViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientRootViewController.swift; sourceTree = ""; };
DC3BE0E02077CD4B002A0AC0 /* Synchronized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Synchronized.swift; sourceTree = ""; };
+ DC3DDF02287E1AC200E5586D /* UIViewController+HostBundleID.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+HostBundleID.h"; sourceTree = ""; };
+ DC3DDF03287E1AC200E5586D /* UIViewController+HostBundleID.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+HostBundleID.m"; sourceTree = ""; };
DC3DEC7A22AFA1F000F3352D /* DownloadItemsHUDViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadItemsHUDViewController.swift; sourceTree = ""; };
DC3DEC7C22AFFE8E00F3352D /* KVOWaiter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KVOWaiter.swift; sourceTree = ""; };
DC3DEC7F22B03AE700F3352D /* CardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardViewController.swift; sourceTree = ""; };
@@ -2329,6 +2333,15 @@
path = Client;
sourceTree = "";
};
+ DC3DDEFE287E1AA500E5586D /* UIKit Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ DC3DDF03287E1AC200E5586D /* UIViewController+HostBundleID.m */,
+ DC3DDF02287E1AC200E5586D /* UIViewController+HostBundleID.h */,
+ );
+ path = "UIKit Extensions";
+ sourceTree = "";
+ };
DC422448207CAED60006A2A6 /* Theming */ = {
isa = PBXGroup;
children = (
@@ -2651,6 +2664,7 @@
DCC832E5242CB14E00153F8C /* Notifications */,
DC774E5422F44DF6000B11A1 /* SDK Extensions */,
DC0030BE2350B1CE00BB8570 /* Tools */,
+ DC3DDEFE287E1AA500E5586D /* UIKit Extensions */,
DC774E5B22F44E4A000B11A1 /* ZIP Archive */,
DC774E6522F44EA7000B11A1 /* Resources */,
);
@@ -3157,6 +3171,7 @@
DCFEFE2A236876BD009A142F /* OCLicenseManager.h in Headers */,
DCFEFE4923687C83009A142F /* OCLicenseEntitlement.h in Headers */,
DC4332002472E1B4002DC0E5 /* OCLicenseEMMProvider.h in Headers */,
+ DC3DDF07287E1C0E00E5586D /* UIViewController+HostBundleID.h in Headers */,
DCFEFE39236877A7009A142F /* OCLicenseFeature.h in Headers */,
DC23D1DA238F391200423F62 /* OCLicenseAppStoreReceipt.h in Headers */,
DC70398526128B89009F2DC1 /* NSString+ByteCountParser.h in Headers */,
@@ -4292,6 +4307,7 @@
DCFEFE50236880B5009A142F /* OCLicenseOffer.m in Sources */,
DC0030C12350B1CE00BB8570 /* NSData+Encoding.m in Sources */,
DC774E5F22F44E57000B11A1 /* ZIPArchive.m in Sources */,
+ DC3DDF06287E1C0800E5586D /* UIViewController+HostBundleID.m in Sources */,
DCDBB60B2525306000FAD707 /* NotificationAuthErrorForwarder.m in Sources */,
DCD71E8027427463001592C6 /* BuildOptions.m in Sources */,
DC080CE5238AE3F40044C5D2 /* OCLicenseAppStoreProvider.m in Sources */,
@@ -4670,8 +4686,8 @@
APP_BUILD_FLAGS = "$(inherited)";
APP_BUILD_FLAGS_SWIFT = "$(APP_BUILD_FLAGS)";
APP_PRODUCT_NAME = ownCloud;
- APP_SHORT_VERSION = 11.10.0;
- APP_VERSION = 217;
+ APP_SHORT_VERSION = 11.10.1;
+ APP_VERSION = 225;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@@ -4739,8 +4755,8 @@
APP_BUILD_FLAGS = "$(inherited)";
APP_BUILD_FLAGS_SWIFT = "$(APP_BUILD_FLAGS)";
APP_PRODUCT_NAME = ownCloud;
- APP_SHORT_VERSION = 11.10.0;
- APP_VERSION = 217;
+ APP_SHORT_VERSION = 11.10.1;
+ APP_VERSION = 225;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
diff --git a/ownCloud/Release Notes/ReleaseNotes.plist b/ownCloud/Release Notes/ReleaseNotes.plist
index 1b56de0f7..8b5391988 100644
--- a/ownCloud/Release Notes/ReleaseNotes.plist
+++ b/ownCloud/Release Notes/ReleaseNotes.plist
@@ -1613,6 +1613,23 @@ Added an optional "Wait for completion" option to the "Save File&
+
+ Version
+ 11.10.1
+ ReleaseNotes
+
+
+ Title
+ Available Offline Folders
+ Subtitle
+ Shows the contents of the available folders when offline.
+ Type
+ Fix
+ ImageName
+ wrench
+
+
+
diff --git a/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift b/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift
index ec3f65406..359a1845f 100644
--- a/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift
+++ b/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift
@@ -118,7 +118,7 @@ class StaticLoginSetupViewController : StaticLoginStepViewController {
onboardingSection = StaticTableViewSection(headerTitle: nil, identifier: "onboardingSection")
if let message = profile.promptForHelpURL, let title = profile.helpURLButtonString {
- let (proceedButton, _) = onboardingSection.addButtonFooter(message: message, messageItemStyle: .welcomeMessage, proceedLabel: title, proceedItemStyle: .informal, cancelLabel: nil)
+ let (proceedButton, _) = onboardingSection.addButtonFooter(message: message, messageItemStyle: .welcomeMessage, proceedLabel: title, proceedItemStyle: .welcomeInformal, cancelLabel: nil)
proceedButton?.addTarget(self, action: #selector(self.helpAction), for: .touchUpInside)
}
diff --git a/ownCloudAppFramework/AppLock Settings/AppLockSettings.h b/ownCloudAppFramework/AppLock Settings/AppLockSettings.h
index 13fc01a6c..941153565 100644
--- a/ownCloudAppFramework/AppLock Settings/AppLockSettings.h
+++ b/ownCloudAppFramework/AppLock Settings/AppLockSettings.h
@@ -31,6 +31,8 @@ NS_ASSUME_NONNULL_BEGIN
@property(assign,nonatomic) BOOL lockEnabled;
@property(assign,nonatomic) NSInteger lockDelay;
@property(assign,nonatomic) BOOL biometricalSecurityEnabled;
+@property(assign,nonatomic) BOOL biometricalSecurityEnabledinShareSheet;
+@property(readonly,nonatomic,nullable) NSURL *biometricalAuthenticationRedirectionTargetURL;
@property(readonly,nonatomic) BOOL isPasscodeEnforced;
@property(readonly,nonatomic) NSInteger requiredPasscodeDigits;
@@ -46,5 +48,6 @@ extern OCClassSettingsKey OCClassSettingsKeyRequiredPasscodeDigits;
extern OCClassSettingsKey OCClassSettingsKeyMaximumPasscodeDigits;
extern OCClassSettingsKey OCClassSettingsKeyPasscodeLockDelay;
extern OCClassSettingsKey OCClassSettingsKeyPasscodeUseBiometricalUnlock;
+extern OCClassSettingsKey OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp;
NS_ASSUME_NONNULL_END
diff --git a/ownCloudAppFramework/AppLock Settings/AppLockSettings.m b/ownCloudAppFramework/AppLock Settings/AppLockSettings.m
index 46622d520..25c57542d 100644
--- a/ownCloudAppFramework/AppLock Settings/AppLockSettings.m
+++ b/ownCloudAppFramework/AppLock Settings/AppLockSettings.m
@@ -17,6 +17,7 @@
*/
#import "AppLockSettings.h"
+#import "Branding.h"
@implementation AppLockSettings
@@ -54,7 +55,19 @@ + (OCClassSettingsIdentifier)classSettingsIdentifier
OCClassSettingsKeyPasscodeEnforced : @(NO),
OCClassSettingsKeyRequiredPasscodeDigits : @(4),
OCClassSettingsKeyMaximumPasscodeDigits : @(6),
- OCClassSettingsKeyPasscodeUseBiometricalUnlock : @(NO)
+ OCClassSettingsKeyPasscodeUseBiometricalUnlock : @(NO),
+ OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp : @{
+ @"default" : @{
+ @"allow" : @(YES)
+ },
+
+ // For unknown reasons invoking biometric authentication from the
+ // share sheet in Boxer leads to dismissal of the entire share sheet,
+ // so (as of July 2022) we hardcode it as an exception here
+ @"com.air-watch.boxer" : @{
+ @"allow" : @(NO)
+ }
+ }
});
}
@@ -89,9 +102,16 @@ + (OCClassSettingsMetadataCollection)classSettingsMetadata
OCClassSettingsMetadataKeyCategory : @"Passcode"
},
- OCClassSettingsKeyPasscodeUseBiometricalUnlock : @{
- OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeBoolean,
- OCClassSettingsMetadataKeyDescription : @"Controls wether the biometrical unlock will be enabled automatically.",
+ OCClassSettingsKeyPasscodeUseBiometricalUnlock : @{
+ OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeBoolean,
+ OCClassSettingsMetadataKeyDescription : @"Controls wether the biometrical unlock will be enabled automatically.",
+ OCClassSettingsMetadataKeyStatus : OCClassSettingsKeyStatusAdvanced,
+ OCClassSettingsMetadataKeyCategory : @"Passcode"
+ },
+
+ OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp : @{
+ OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeDictionary,
+ OCClassSettingsMetadataKeyDescription : @"Controls the biometrical unlock availability in the share sheet, with per-app level control.",
OCClassSettingsMetadataKeyStatus : OCClassSettingsKeyStatusAdvanced,
OCClassSettingsMetadataKeyCategory : @"Passcode"
}
@@ -128,14 +148,28 @@ - (void)setLockDelay:(NSInteger)lockDelay
- (BOOL)biometricalSecurityEnabled
{
- NSNumber *useBiometricalUnlock;
+ NSNumber *useBiometricalUnlockNumber;
+ BOOL useBiometricalUnlock = NO;
- if ((useBiometricalUnlock = [_userDefaults objectForKey:@"security-settings-use-biometrical"]) != nil)
+ if ((useBiometricalUnlockNumber = [_userDefaults objectForKey:@"security-settings-use-biometrical"]) != nil)
{
- return (useBiometricalUnlock.boolValue);
+ useBiometricalUnlock = useBiometricalUnlockNumber.boolValue;
+ }
+ else
+ {
+ useBiometricalUnlock = [[self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeUseBiometricalUnlock] boolValue];
}
- return ([[self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeUseBiometricalUnlock] boolValue]);
+ if (useBiometricalUnlock)
+ {
+ // Apple share extension specific settings
+ if ([OCAppIdentity.sharedAppIdentity.componentIdentifier isEqual:OCAppComponentIdentifierShareExtension])
+ {
+ return ([self biometricalSecurityEnabledinShareSheet]);
+ }
+ }
+
+ return (useBiometricalUnlock);
}
- (void)setBiometricalSecurityEnabled:(BOOL)biometricalSecurityEnabled
@@ -143,6 +177,102 @@ - (void)setBiometricalSecurityEnabled:(BOOL)biometricalSecurityEnabled
[_userDefaults setBool:biometricalSecurityEnabled forKey:@"security-settings-use-biometrical"];
}
+- (NSDictionary *)_shareSheetBiometricalAttributesForApp:(NSString *)hostAppID
+{
+ NSDictionary *shareSheetBiometricalUnlockByApp = [self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp];
+ NSDictionary *attributesForApp = nil;
+
+ if ([shareSheetBiometricalUnlockByApp isKindOfClass:NSDictionary.class])
+ {
+ if (shareSheetBiometricalUnlockByApp[hostAppID] != nil)
+ {
+ attributesForApp = OCTypedCast(shareSheetBiometricalUnlockByApp[hostAppID], NSDictionary);
+ }
+ else
+ {
+ attributesForApp = OCTypedCast(shareSheetBiometricalUnlockByApp[@"default"], NSDictionary);
+ }
+ }
+
+ return (attributesForApp);
+}
+
+- (NSDictionary *)_shareSheetBiometricalAttributes
+{
+ NSString *hostAppID;
+
+ if ((hostAppID = OCAppIdentity.sharedAppIdentity.hostAppBundleIdentifier) == nil)
+ {
+ hostAppID = @"default";
+ }
+
+ return ([self _shareSheetBiometricalAttributesForApp:hostAppID]);
+}
+
+- (BOOL)biometricalSecurityEnabledinShareSheet
+{
+ NSNumber *useBiometricalUnlock;
+
+ if ((useBiometricalUnlock = [_userDefaults objectForKey:@"security-settings-use-biometrical-share-sheet"]) != nil)
+ {
+ return (useBiometricalUnlock.boolValue);
+ }
+
+ NSDictionary *shareSheetAttributesForApp = nil;
+
+ if ((shareSheetAttributesForApp = [self _shareSheetBiometricalAttributes]) != nil)
+ {
+ NSNumber *enabled;
+
+ if ((enabled = OCTypedCast(shareSheetAttributesForApp[@"allow"], NSNumber)) != nil)
+ {
+ return (enabled.boolValue);
+ }
+ }
+
+ return (YES);
+}
+
+- (void)setBiometricalSecurityEnabledinShareSheet:(BOOL)biometricalSecurityEnabledinShareSheet
+{
+ [_userDefaults setBool:biometricalSecurityEnabledinShareSheet forKey:@"security-settings-use-biometrical-share-sheet"];
+}
+
+- (NSURL *)biometricalAuthenticationRedirectionTargetURL
+{
+ if ([OCAppIdentity.sharedAppIdentity.componentIdentifier isEqual:OCAppComponentIdentifierShareExtension])
+ {
+ // Only in share extension
+ NSDictionary *shareSheetAttributesForApp = nil;
+
+ if ((shareSheetAttributesForApp = [self _shareSheetBiometricalAttributes]) != nil)
+ {
+ NSString *trampolineURLString;
+
+ // For apps with a trampoline URL, determine the target URL to initiate the authentication trampoline
+ if ((trampolineURLString = shareSheetAttributesForApp[@"trampoline-url"]) != nil)
+ {
+ NSString *toAppURLScheme;
+
+ if ((toAppURLScheme = [Branding.sharedBranding appURLSchemesForBundleURLName:nil].firstObject) != nil)
+ {
+ NSString *targetURLString = [NSString stringWithFormat:@"%@://?authenticateForApp=%@", toAppURLScheme, OCAppIdentity.sharedAppIdentity.hostAppBundleIdentifier];
+
+ return ([NSURL URLWithString:targetURLString]);
+ }
+ }
+ }
+ }
+
+ return (nil);
+}
+
+// Counterpart to .biometricalAuthenticationRedirectionTargetURL for use in the app (not implemented)
+//- (NSURL *)biometricalAuthenticationReturnURL
+//{
+// return (nil);
+//}
+
- (BOOL)isPasscodeEnforced
{
NSNumber *isPasscodeEnforced = [self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeEnforced];
@@ -190,3 +320,4 @@ - (BOOL)lockDelayUserSettable
OCClassSettingsKey OCClassSettingsKeyMaximumPasscodeDigits = @"maximumPasscodeDigits";
OCClassSettingsKey OCClassSettingsKeyPasscodeLockDelay = @"lockDelay";
OCClassSettingsKey OCClassSettingsKeyPasscodeUseBiometricalUnlock = @"use-biometrical-unlock";
+OCClassSettingsKey OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp = @"share-sheet-biometrical-unlock-by-app";
diff --git a/ownCloudAppFramework/Branding/Branding.h b/ownCloudAppFramework/Branding/Branding.h
index 20ddc801f..8c7a00faa 100644
--- a/ownCloudAppFramework/Branding/Branding.h
+++ b/ownCloudAppFramework/Branding/Branding.h
@@ -44,6 +44,8 @@ typedef NSString* BrandingImageName NS_TYPED_EXTENSIBLE_ENUM;
@property(strong,nullable,nonatomic,readonly) NSBundle *appBundle; //!< Bundle of the main app
+- (NSArray *)appURLSchemesForBundleURLName:(nullable NSString *)bundleURLName; //!< URL schemes from the app's Info.plist matching the provided CFBundleURLName.
+
@property(strong) NSDictionary *legacyKeyPathsByClassSettingsKeys;
- (void)registerLegacyKeyPath:(BrandingLegacyKeyPath)keyPath forClassSettingsKey:(OCClassSettingsKey)classSettingsKey;
diff --git a/ownCloudAppFramework/Branding/Branding.m b/ownCloudAppFramework/Branding/Branding.m
index c66bcdb37..03e2268ce 100644
--- a/ownCloudAppFramework/Branding/Branding.m
+++ b/ownCloudAppFramework/Branding/Branding.m
@@ -260,6 +260,35 @@ - (NSDictionary *)userDefaultsDefaultValues
return ([self computedValueForClassSettingsKey:BrandingKeyUserDefaultsDefaultValues]);
}
+- (NSArray *)appURLSchemesForBundleURLName:(nullable NSString *)bundleURLName
+{
+ NSBundle *appBundle;
+ NSMutableArray *appURLSchemes = [NSMutableArray new];
+
+ if ((appBundle = self.appBundle) != nil)
+ {
+ NSArray *urlSchemeDictionaries;
+
+ if ((urlSchemeDictionaries = [appBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]) != nil)
+ {
+ for (NSDictionary *urlSchemesDict in urlSchemeDictionaries)
+ {
+ if ((bundleURLName == nil) || [bundleURLName isEqual:urlSchemesDict[@"CFBundleURLName"]])
+ {
+ NSArray *urlSchemes;
+
+ if ((urlSchemes = urlSchemesDict[@"CFBundleURLSchemes"]) != nil)
+ {
+ [appURLSchemes addObjectsFromArray:urlSchemes];
+ }
+ }
+ }
+ }
+ }
+
+ return (appURLSchemes);
+}
+
- (NSArray *)disabledImportMethods
{
return ([self computedValueForClassSettingsKey:BrandingKeyDisabledImportMethods]);
diff --git a/ownCloudAppFramework/UIKit Extensions/UIViewController+HostBundleID.h b/ownCloudAppFramework/UIKit Extensions/UIViewController+HostBundleID.h
new file mode 100644
index 000000000..5b081780c
--- /dev/null
+++ b/ownCloudAppFramework/UIKit Extensions/UIViewController+HostBundleID.h
@@ -0,0 +1,29 @@
+//
+// UIViewController+HostBundleID.h
+// ownCloud
+//
+// Created by Felix Schwarz on 12.07.22.
+// Copyright © 2022 ownCloud GmbH. All rights reserved.
+//
+
+/*
+ * Copyright (C) 2022, ownCloud GmbH.
+ *
+ * This code is covered by the GNU Public License Version 3.
+ *
+ * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/
+ * You should have received a copy of this license along with this program. If not, see .
+ *
+ */
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface UIViewController (HostBundleID)
+
+@property(nullable,readonly) NSString *oc_hostAppBundleIdentifier;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ownCloudAppFramework/UIKit Extensions/UIViewController+HostBundleID.m b/ownCloudAppFramework/UIKit Extensions/UIViewController+HostBundleID.m
new file mode 100644
index 000000000..7a8711c95
--- /dev/null
+++ b/ownCloudAppFramework/UIKit Extensions/UIViewController+HostBundleID.m
@@ -0,0 +1,34 @@
+//
+// UIViewController+HostBundleID.m
+// ownCloud
+//
+// Created by Felix Schwarz on 12.07.22.
+// Copyright © 2022 ownCloud GmbH. All rights reserved.
+//
+
+/*
+ * Copyright (C) 2022, ownCloud GmbH.
+ *
+ * This code is covered by the GNU Public License Version 3.
+ *
+ * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/
+ * You should have received a copy of this license along with this program. If not, see .
+ *
+ */
+
+#import "UIViewController+HostBundleID.h"
+#import
+
+@implementation UIViewController (HostBundleID)
+
+- (NSString *)oc_hostAppBundleIdentifier
+{
+ @try {
+ return ([self valueForKey:@"_hostBundleID"]);
+ } @catch (NSException *exception) {
+ }
+
+ return (nil);
+}
+
+@end
diff --git a/ownCloudAppFramework/ownCloudApp.h b/ownCloudAppFramework/ownCloudApp.h
index 6ca94e137..2f90e6990 100644
--- a/ownCloudAppFramework/ownCloudApp.h
+++ b/ownCloudAppFramework/ownCloudApp.h
@@ -36,6 +36,8 @@ FOUNDATION_EXPORT const unsigned char ownCloudAppVersionString[];
#import
#import
+#import
+
#import
#import
#import
diff --git a/ownCloudAppShared/AppLock/AppLockManager.swift b/ownCloudAppShared/AppLock/AppLockManager.swift
index e35c11d16..4e4b169f8 100644
--- a/ownCloudAppShared/AppLock/AppLockManager.swift
+++ b/ownCloudAppShared/AppLock/AppLockManager.swift
@@ -37,8 +37,16 @@ public class AppLockManager: NSObject {
// MARK: - State
private var lastApplicationBackgroundedDate : Date? {
- didSet {
- if let date = lastApplicationBackgroundedDate {
+ get {
+ if let archivedData = self.keychain?.readDataFromKeychainItem(forAccount: keychainAccount, path: keychainLockedDate) {
+ guard let value = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSDate.self, from: archivedData) else { return nil }
+ return value as? Date
+ }
+
+ return nil
+ }
+ set(newValue) {
+ if let date = newValue {
let archivedData = try? NSKeyedArchiver.archivedData(withRootObject: date as NSDate, requiringSecureCoding: true)
self.keychain?.write(archivedData, toKeychainItemForAccount: keychainAccount, path: keychainLockedDate)
} else {
@@ -47,9 +55,17 @@ public class AppLockManager: NSObject {
}
}
- public var unlocked: Bool = false {
- didSet {
- let archivedData = try? NSKeyedArchiver.archivedData(withRootObject: unlocked as NSNumber, requiringSecureCoding: true)
+ public var unlocked: Bool {
+ get {
+ if let archivedData = self.keychain?.readDataFromKeychainItem(forAccount: keychainAccount, path: keychainUnlocked) {
+ guard let value = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSNumber.self, from: archivedData)?.boolValue else { return false}
+ return value ?? false
+ }
+
+ return false
+ }
+ set(newValue) {
+ let archivedData = try? NSKeyedArchiver.archivedData(withRootObject: newValue as NSNumber, requiringSecureCoding: true)
self.keychain?.write(archivedData, toKeychainItemForAccount: keychainAccount, path: keychainUnlocked)
}
}
@@ -124,6 +140,10 @@ public class AppLockManager: NSObject {
// Set a view controller only, if you want to use it in an extension, when UIWindow is not working
public var passwordViewHostViewController: UIViewController?
+ private var biometricalSecurityEnabled: Bool {
+ return AppLockSettings.shared.biometricalSecurityEnabled
+ }
+
// MARK: - Init
public static var shared = AppLockManager()
@@ -148,7 +168,7 @@ public class AppLockManager: NSObject {
}
// MARK: - Show / Dismiss Passcode View
- public func showLockscreenIfNeeded(forceShow: Bool = false, setupMode: Bool = false, context: LAContext = LAContext()) {
+ public func showLockscreenIfNeeded(forceShow: Bool = false, setupMode: Bool = false, context: LAContext? = nil) {
if self.shouldDisplayLockscreen || forceShow || setupMode {
lockscreenOpenForced = forceShow
lockscreenOpen = true
@@ -159,6 +179,8 @@ public class AppLockManager: NSObject {
} else if setupMode {
showBiometricalAuthenticationInterface(context: context)
}
+ } else {
+ dismissLockscreen(animated: true)
}
}
@@ -296,15 +318,14 @@ public class AppLockManager: NSObject {
passcodeViewController = PasscodeViewController(biometricalHandler: { (passcodeViewController) in
if !self.shouldDisplayCountdown {
- let context = LAContext()
- self.showBiometricalAuthenticationInterface(context: context)
+ self.showBiometricalAuthenticationInterface()
}
}, completionHandler: { (viewController: PasscodeViewController, passcode: String) in
self.attemptUnlock(with: passcode, passcodeViewController: viewController)
}, requiredLength: AppLockManager.shared.passcode?.count ?? AppLockSettings.shared.requiredPasscodeDigits)
passcodeViewController.message = "Enter code".localized
- passcodeViewController.cancelButtonHidden = false
+ passcodeViewController.cancelButtonAvailable = false
passcodeViewController.screenBlurringEnabled = lockscreenOpenForced && !self.shouldDisplayLockscreen
@@ -313,14 +334,19 @@ public class AppLockManager: NSObject {
// MARK: - App Events
@objc func appDidEnterBackground() {
- lastApplicationBackgroundedDate = Date()
+ if unlocked {
+ lastApplicationBackgroundedDate = Date()
+ } else {
+ lastApplicationBackgroundedDate = nil
+ }
showLockscreenIfNeeded(forceShow: true)
}
@objc func appWillEnterForeground() {
if self.shouldDisplayLockscreen {
- showLockscreenIfNeeded()
+ dismissLockscreen(animated: false)
+ self.showLockscreenIfNeeded()
} else {
dismissLockscreen(animated: false)
}
@@ -330,11 +356,11 @@ public class AppLockManager: NSObject {
func attemptUnlock(with testPasscode: String?, customErrorMessage: String? = nil, passcodeViewController: PasscodeViewController? = nil) {
if testPasscode == self.passcode {
unlocked = true
- lastApplicationBackgroundedDate = nil
failedPasscodeAttempts = 0
dismissLockscreen(animated: true)
} else {
unlocked = false
+ lastApplicationBackgroundedDate = nil
passcodeViewController?.errorMessage = (customErrorMessage != nil) ? customErrorMessage! : "Incorrect code".localized
failedPasscodeAttempts += 1
@@ -455,8 +481,20 @@ public class AppLockManager: NSObject {
// MARK: - Biometrical Unlock
private var biometricalAuthenticationInterfaceShown : Bool = false
- func showBiometricalAuthenticationInterface(context: LAContext) {
- if shouldDisplayLockscreen, AppLockSettings.shared.biometricalSecurityEnabled, !biometricalAuthenticationInterfaceShown {
+ func showBiometricalAuthenticationInterface(context inContext: LAContext? = nil) {
+
+ if shouldDisplayLockscreen, biometricalSecurityEnabled, !biometricalAuthenticationInterfaceShown {
+ // Check if we should perform biometrical authentication - or redirect
+ if let targetURL = AppLockSettings.shared.biometricalAuthenticationRedirectionTargetURL {
+ // Unfortunately, opening the URL closes the share sheet just like invoking
+ // biometric auth - so in those instances where we'd want to use it to work around
+ // that.
+ self.passwordViewHostViewController?.openURL(targetURL)
+ return
+ }
+
+ // Perform biometrical authentication
+ let context = inContext ?? LAContext()
var evaluationError: NSError?
// Check if the device can evaluate the policy.
@@ -517,7 +555,7 @@ public class AppLockManager: NSObject {
}
}
} else {
- if let error = evaluationError, AppLockSettings.shared.biometricalSecurityEnabled {
+ if let error = evaluationError, biometricalSecurityEnabled {
OnMainThread {
self.performPasscodeViewControllerUpdates { (passcodeViewController) in
passcodeViewController.errorMessage = error.localizedDescription
diff --git a/ownCloudAppShared/AppLock/PasscodeViewController.swift b/ownCloudAppShared/AppLock/PasscodeViewController.swift
index fe5157afa..1473f4541 100644
--- a/ownCloudAppShared/AppLock/PasscodeViewController.swift
+++ b/ownCloudAppShared/AppLock/PasscodeViewController.swift
@@ -107,18 +107,18 @@ public class PasscodeViewController: UIViewController, Themeable {
}
}
- var cancelButtonHidden: Bool {
+ var cancelButtonAvailable: Bool {
didSet {
- cancelButton?.isEnabled = cancelButtonHidden
- cancelButton?.isHidden = !cancelButtonHidden
+ cancelButton?.isEnabled = cancelButtonAvailable
+ cancelButton?.isHidden = !cancelButtonAvailable
}
}
var biometricalButtonHidden: Bool = false {
didSet {
- biometricalButton?.isEnabled = biometricalButtonHidden
- biometricalButton?.isHidden = !biometricalButtonHidden
- biometricalImageView?.isHidden = !biometricalButtonHidden
+ biometricalButton?.isEnabled = !biometricalButtonHidden
+ biometricalButton?.isHidden = biometricalButtonHidden
+ biometricalImageView?.isHidden = biometricalButtonHidden
biometricalImageView?.image = LAContext().biometricsAuthenticationImage()
}
}
@@ -142,7 +142,7 @@ public class PasscodeViewController: UIViewController, Themeable {
self.biometricalHandler = biometricalHandler
self.completionHandler = completionHandler
self.keypadButtonsEnabled = keypadButtonsEnabled
- self.cancelButtonHidden = hasCancelButton
+ self.cancelButtonAvailable = hasCancelButton
self.keypadButtonsHidden = false
self.screenBlurringEnabled = false
self.passcodeLength = requiredLength
@@ -167,13 +167,13 @@ public class PasscodeViewController: UIViewController, Themeable {
self.errorMessage = { self.errorMessage }()
self.timeoutMessage = { self.timeoutMessage }()
- self.cancelButtonHidden = { self.cancelButtonHidden }()
+ self.cancelButtonAvailable = { self.cancelButtonAvailable }()
self.keypadButtonsEnabled = { self.keypadButtonsEnabled }()
self.keypadButtonsHidden = { self.keypadButtonsHidden }()
self.screenBlurringEnabled = { self.screenBlurringEnabled }()
self.errorMessageLabel?.minimumScaleFactor = 0.5
self.errorMessageLabel?.adjustsFontSizeToFitWidth = true
- self.biometricalButtonHidden = !((!AppLockSettings.shared.biometricalSecurityEnabled || !AppLockSettings.shared.lockEnabled) || self.cancelButtonHidden)
+ self.biometricalButtonHidden = (!AppLockSettings.shared.biometricalSecurityEnabled || !AppLockSettings.shared.lockEnabled || cancelButtonAvailable) // cancelButtonAvailable is true for setup tasks/settings changes only
updateKeypadButtons()
if let biometricalSecurityName = LAContext().supportedBiometricsAuthenticationName() {
self.biometricalButton?.accessibilityLabel = biometricalSecurityName
diff --git a/ownCloudAppShared/Client/File Lists/QueryFileListTableViewController.swift b/ownCloudAppShared/Client/File Lists/QueryFileListTableViewController.swift
index 430793be9..28463c2d5 100644
--- a/ownCloudAppShared/Client/File Lists/QueryFileListTableViewController.swift
+++ b/ownCloudAppShared/Client/File Lists/QueryFileListTableViewController.swift
@@ -31,6 +31,17 @@ public extension OCQueryState {
}
}
+public extension OCCoreConnectionStatus {
+ var isOffline: Bool {
+ switch self {
+ case .offline, .unavailable:
+ return true
+ default:
+ return false
+ }
+ }
+}
+
public protocol MultiSelectSupport {
func setupMultiselection()
func enterMultiselection()
@@ -296,32 +307,37 @@ open class QueryFileListTableViewController: FileListTableViewController, SortBa
self.actionContext = ActionContext(viewController: self, core: core, query: query, items: [OCItem](), location: actionsLocation)
}
- switch query.state {
- case .contentsFromCache, .idle, .waitingForServerReply:
- if previousItemCount == 0, self.items.count == 0, query.state == .waitingForServerReply {
- break
- }
-
- if query.state.isFinal {
- if self.items.count == 0 {
- if self.searchController?.searchBar.text != "" {
- self.messageView?.message(show: true, with: UIEdgeInsets(top: sortBar?.frame.size.height ?? 0, left: 0, bottom: 0, right: 0), imageName: "icon-search", title: "No matches".localized, message: "There is no results for this search".localized)
- } else {
- self.messageView?.message(show: true, imageName: "folder", title: "Empty folder".localized, message: "This folder contains no files or folders.".localized)
- }
- } else {
- self.messageView?.message(show: false)
- }
-
- self.reloadTableData()
- }
- case .targetRemoved:
- self.messageView?.message(show: true, imageName: "folder", title: "Folder removed".localized, message: "This folder no longer exists on the server.".localized)
- self.reloadTableData()
-
- default:
- self.messageView?.message(show: false)
- }
+ switch query.state {
+ case .contentsFromCache, .idle, .waitingForServerReply:
+ let latestItemCount = self.items.count
+
+ if previousItemCount == 0, latestItemCount == 0, query.state == .waitingForServerReply {
+ break
+ }
+
+ // Refresh on:
+ if (previousItemCount != latestItemCount) || // - item count change
+ query.state.isFinal || // - query state is final (== anything but .waitingForServerReply and .started)
+ ((query.state == .waitingForServerReply) && (core?.connectionStatus.isOffline == true)) { // - when waiting for a server reply while the connection is offline
+ if latestItemCount == 0 {
+ if self.searchController?.searchBar.text != "" {
+ self.messageView?.message(show: true, with: UIEdgeInsets(top: sortBar?.frame.size.height ?? 0, left: 0, bottom: 0, right: 0), imageName: "icon-search", title: "No matches".localized, message: "There is no results for this search".localized)
+ } else {
+ self.messageView?.message(show: true, imageName: "folder", title: "Empty folder".localized, message: "This folder contains no files or folders.".localized)
+ }
+ } else {
+ self.messageView?.message(show: false)
+ }
+
+ self.reloadTableData()
+ }
+ case .targetRemoved:
+ self.messageView?.message(show: true, imageName: "folder", title: "Folder removed".localized, message: "This folder no longer exists on the server.".localized)
+ self.reloadTableData()
+
+ default:
+ self.messageView?.message(show: false)
+ }
}
// MARK: - Themeable
diff --git a/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift b/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift
index 53c2d4a2c..069d312f4 100644
--- a/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift
+++ b/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift
@@ -100,4 +100,10 @@ extension UIColor {
return (String(format: "\(leadIn)%02x%02x%02x", Int(selfRed*255.0), Int(selfGreen*255.0), Int(selfBlue*255.0)))
}
+
+ public func isLight() -> Bool {
+ guard let components = cgColor.components, components.count > 2 else {return false}
+ let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
+ return (brightness > 0.5)
+ }
}
diff --git a/ownCloudAppShared/UIKit Extension/UIViewController+Extension.swift b/ownCloudAppShared/UIKit Extension/UIViewController+Extension.swift
index e024d90ce..a685ff4e9 100644
--- a/ownCloudAppShared/UIKit Extension/UIViewController+Extension.swift
+++ b/ownCloudAppShared/UIKit Extension/UIViewController+Extension.swift
@@ -55,4 +55,15 @@ public extension UIViewController {
return self
}
+
+ @objc @discardableResult func openURL(_ url: URL) -> Bool {
+ var responder: UIResponder? = self.navigationController
+ while responder != nil {
+ if let application = responder as? UIApplication {
+ return application.perform(#selector(openURL(_:)), with: url) != nil
+ }
+ responder = responder?.next
+ }
+ return true
+ }
}
diff --git a/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift b/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift
index e2deb87c7..a81f8099f 100644
--- a/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift
+++ b/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift
@@ -43,6 +43,7 @@ public enum ThemeItemStyle {
case purchase
case welcome
+ case welcomeInformal
}
public enum ThemeItemState {
@@ -86,9 +87,14 @@ public extension NSObject {
case .purchase:
themeButton.themeColorCollection = collection.purchaseColors
-
- case .welcome:
- themeButton.themeColorCollection = collection.loginColors.filledColorPairCollection
+
+ case .welcome:
+ themeButton.themeColorCollection = collection.loginColors.filledColorPairCollection
+
+ case .welcomeInformal:
+ let fromPair = collection.loginColors.filledColorPairCollection
+ let normal = ThemeColorPair(foreground: fromPair.normal.foreground.lighter(0.25), background: fromPair.normal.background.lighter(0.25))
+ themeButton.themeColorCollection = ThemeColorPairCollection(fromPair: normal)
case .informal:
themeButton.themeColorCollection = collection.informalColors.filledColorPairCollection
@@ -210,6 +216,7 @@ public extension NSObject {
case .welcomeMessage:
normalColor = collection.loginColors.secondaryLabelColor
+ normalColor = collection.loginColors.secondaryLabelColor
highlightColor = collection.loginColors.secondaryLabelColor
case .message, .bigMessage:
diff --git a/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift b/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift
index 653f6b256..cd1e87478 100644
--- a/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift
+++ b/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift
@@ -397,7 +397,16 @@ public class ThemeCollection : NSObject {
self.loginColors = colors.resolveThemeColorCollection("Login", self.darkBrandColors)
// Bar styles
- self.statusBarStyle = styleResolver.resolveStatusBarStyle(for: "statusBarStyle", fallback: .lightContent)
+ var defaultStatusBarStyle : UIStatusBarStyle = .lightContent
+ if let backgroundColor = self.navigationBarColors.backgroundColor, backgroundColor.isLight() {
+ if #available(iOSApplicationExtension 13.0, *) {
+ defaultStatusBarStyle = .darkContent
+ } else {
+ defaultStatusBarStyle = .default
+ }
+ }
+
+ self.statusBarStyle = styleResolver.resolveStatusBarStyle(for: "statusBarStyle", fallback: defaultStatusBarStyle)
self.loginStatusBarStyle = styleResolver.resolveStatusBarStyle(for: "loginStatusBarStyle", fallback: self.statusBarStyle)
self.barStyle = styleResolver.resolveBarStyle(fallback: .black)
@@ -411,10 +420,10 @@ public class ThemeCollection : NSObject {
// Logo fill color
logoFillColor = UIColor.lightGray
- if lightBrandColor.isEqual(UIColor(hex: 0xFFFFFF)) {
+ if lightBrandColor.isLight() {
self.neutralColors.normal.background = self.darkBrandColor
self.lightBrandColors.filledColorPairCollection.normal.background = self.darkBrandColor
- }
+ }
}
self.informalColors = colors.resolveThemeColorCollection("Informal", self.lightBrandColors)