From 585f4a852d27fb4290bd83f144cad58321b3ebcd Mon Sep 17 00:00:00 2001 From: Arnfried Griesert Date: Tue, 17 Mar 2026 13:55:15 +0000 Subject: [PATCH] Feature/7555 migration part 3 --- Config/BWIBuildSettings.swift | 9 ++ .../xcshareddata/xcschemes/Riot.xcscheme | 28 +--- Riot/Assets/de.lproj/Bwi.strings | 13 +- Riot/Assets/en.lproj/Bwi.strings | 28 ++-- Riot/Generated/BWIStrings.swift | 58 ++++++-- .../KeyValueStorage/Extensions/Keychain.swift | 30 ++-- .../PushNotificationService.m | 14 +- .../Home/AllChats/AllChatsCoordinator.swift | 7 + .../AllChats/AllChatsViewController.swift | 18 ++- .../View/AuthenticationLoginScreen.swift | 12 ++ bwi/FeatureBanner/FeatureBannerView.swift | 11 +- bwi/FeatureBanner/MigrationInfoView.swift | 128 ++++++++++++++++-- .../MigrationAssistant.swift | 6 + project.yml | 5 + 14 files changed, 283 insertions(+), 84 deletions(-) diff --git a/Config/BWIBuildSettings.swift b/Config/BWIBuildSettings.swift index 1c9d1de09..3e2f54679 100644 --- a/Config/BWIBuildSettings.swift +++ b/Config/BWIBuildSettings.swift @@ -684,6 +684,15 @@ class BWIBuildSettings: NSObject { @UserDefault(key: UserDefaultsKeys.didBuMXMigrationInfoLevelKey, defaultValue: 0, storage: RiotSettings.defaults) var didShowBuMXMigrationInfoLevel + // Migration level already shown (BWI #8123) + var isManagedViaMDM: Bool { + guard let managedConfig = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed"), + let value = managedConfig["install_bumx_via_mdm"] as? Bool else { + return false + } + + return value + } // shows the grey/green/red shield for the room avatar / user avatar var showEncryptionStatusBadgeOnAvatar = false diff --git a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme index e1775adc4..73493e419 100644 --- a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme +++ b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme @@ -1,13 +1,13 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - - - String { return BWIL10n.tr("Bwi", "bwi_analytics_alert_body", p1) @@ -307,7 +311,15 @@ public class BWIL10n: NSObject { public static var bwiMdmLogoutMessage: String { return BWIL10n.tr("Bwi", "bwi_mdm_logout_message") } - /// Diese App wird bald abgeschaltet und durch eine neue technisch optimierte App ersetzt + /// Alle deine Kontakte und Nachrichten bleiben dabei erhalten. + public static var bwiMobileDialogM1BannerTextBold: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m1_banner_text_bold") + } + /// Das ist eine neue, technisch optimierte App vom BuM. Du kannst sie jetzt schon ausprobieren, bevor bald alle wechseln müssen. + public static var bwiMobileDialogM1BannerTextPrefix: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m1_banner_text_prefix") + } + /// Der BuM wird bald abgeschaltet und durch durch den BuMX ersetzt. Dies ist eine neue, technisch optimierte App public static var bwiMobileDialogM1MoreText1: String { return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m1_more_text_1") } @@ -335,6 +347,30 @@ public class BWIL10n: NSObject { public static var bwiMobileDialogM1MoreTextBullet4: String { return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m1_more_text_bullet_4") } + /// wenige Tage + public static var bwiMobileDialogM2BannerTextBold: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m2_banner_text_bold") + } + /// Das ist eine neue, technisch optimierte App vom BuM. Du hast nur noch + public static var bwiMobileDialogM2BannerTextPrefix: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m2_banner_text_prefix") + } + /// Zeit zu wechseln. Alle deine Kontakte und Nachrichten bleiben dabei erhalten. + public static var bwiMobileDialogM2BannerTextSuffix: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m2_banner_text_suffix") + } + /// Der BuM wurde nun abgeschaltet und durch den BuMX ersetzt. Dies ist eine neue, technisch optimierte App, welche dir von deinem Admin zur Verfügung gestellt wird + public static var bwiMobileDialogM3MoreText1: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m3_more_text_1") + } + /// Wechsel erfolgreich 🎉 + public static var bwiMobileDialogM3SuccessChangeTitle: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m3_success_change_title") + } + /// Diese App kannst du nun löschen. + public static var bwiMobileDialogM3SuccessText: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m3_success_text") + } /// Später erinnern public static var bwiMobileDialogMBannerButton1: String { return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m_banner_button1") @@ -375,13 +411,13 @@ public class BWIL10n: NSObject { public static var bwiMobileDialogMMoreTitle: String { return BWIL10n.tr("Bwi", "bwi_mobile_dialog_m_more_title") } - /// Das ist eine neue, technisch optimierte App vom BuM. Du kannst sie jetzt schon ausprobieren, bevor bald alle wechseln müssen. - public static var bwiMobileMdialogM1BannerText: String { - return BWIL10n.tr("Bwi", "bwi_mobileMdialog_m1_banner_text") + /// Alle deine Kontakte und Nachrichten bleiben dabei bestehen, halte dafür deine Login Daten bereit. + public static var bwiMobileDialogMdmBannerTextBold: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_mdm_banner_text_bold") } - /// Alle deine Kontakte und Nachrichten bleiben dabei erhalten. - public static var bwiMobileMdialogM1BannerTextBold: String { - return BWIL10n.tr("Bwi", "bwi_mobileMdialog_m1_banner_text_bold") + /// Das ist eine neue, technisch optimierte App vom BuM, welche dir bald von deinem Admin zur Verfügung gestellt wird. + public static var bwiMobileDialogMdmBannerTextPrefix: String { + return BWIL10n.tr("Bwi", "bwi_mobile_dialog_mdm_banner_text_prefix") } /// Meine Notizen public static var bwiNotesRoomTitle: String { diff --git a/Riot/Managers/KeyValueStorage/Extensions/Keychain.swift b/Riot/Managers/KeyValueStorage/Extensions/Keychain.swift index 7701d5812..3221509b4 100644 --- a/Riot/Managers/KeyValueStorage/Extensions/Keychain.swift +++ b/Riot/Managers/KeyValueStorage/Extensions/Keychain.swift @@ -9,18 +9,20 @@ import Foundation import KeychainAccess /// Extension on Keychain to get/set booleans +#if !S_BUM_APP extension Keychain { - - public func set(_ value: Bool, key: String, ignoringAttributeSynchronizable: Bool = true) throws { - try set(value.description, key: key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) - } - - public func getBool(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> Bool? { - guard let value = try getString(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) else { - return nil - } - guard value == true.description || value == false.description else { return nil } - return value == true.description - } - -} + + public func set(_ value: Bool, key: String, ignoringAttributeSynchronizable: Bool = true) throws { + try set(value.description, key: key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) + } + + public func getBool(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> Bool? { + guard let value = try getString(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) else { + return nil + } + guard value == true.description || value == false.description else { return nil } + return value == true.description + } + + } +#endif diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index 17039e74c..5170e5420 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -90,9 +90,17 @@ Matrix session observer used to detect new opened sessions. { self.registrationForRemoteNotificationsCompletion = completion; - dispatch_async(dispatch_get_main_queue(), ^{ - [[UIApplication sharedApplication] registerForRemoteNotifications]; - }); + + // BWI #7555 migration part 3 + if([[BWIBuildSettings shared] BuMXMigrationInfoLevel] < 2) { + dispatch_async(dispatch_get_main_queue(), ^{ + // Even after we unregistered from Apples push service we must make sure that + // the app will not register again. After reaching migration level 3 this app should neither show + // remote notifications any more nor register for new notifications. + [[UIApplication sharedApplication] registerForRemoteNotifications]; + }); + } + // BWI #7555 END } - (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken diff --git a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift index b27dc860a..4f5d0a268 100644 --- a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift +++ b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift @@ -775,6 +775,13 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { // BWI #7564 add migration level if let migrationLevel = wellknown?.migrationInfoLevel() { BWIBuildSettings.shared.BuMXMigrationInfoLevel = migrationLevel + + // BWI #7555 migration part 3 + if migrationLevel == 3 { + // Inform Apple that this app on this device can be ignored + UIApplication.shared.unregisterForRemoteNotifications() + } + // BWI #7564 END } // BWI #7564 END self.bwiCheckForMatomoPromt() diff --git a/Riot/Modules/Home/AllChats/AllChatsViewController.swift b/Riot/Modules/Home/AllChats/AllChatsViewController.swift index ff5901520..f0c621804 100644 --- a/Riot/Modules/Home/AllChats/AllChatsViewController.swift +++ b/Riot/Modules/Home/AllChats/AllChatsViewController.swift @@ -249,7 +249,7 @@ class AllChatsViewController: HomeViewController { roomFilterButton?.isSelected = !AllChatsLayoutSettingsManager.shared.allChatLayoutSettings.filters.isEmpty } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -285,6 +285,21 @@ class AllChatsViewController: HomeViewController { NotificationCenter.default.addObserver(self, selector: #selector(self.spaceListDidChange), name: MXSpaceService.didBuildSpaceGraph, object: nil) set(tableHeadeView: self.bannerView) + + // BWI #7555 migration part 3 + if BWIBuildSettings.shared.BuMXMigrationInfoLevel == 3 { + let migrationInfoView = MigrationInfoView(username: "") { + return self.mainSession.myUser.username() + }.environmentObject(BWIThemeService.shared).interactiveDismissDisabled(true) + let hostingViewController = UIHostingController(rootView: migrationInfoView) + if hostingViewController.popoverPresentationController != nil { + hostingViewController.modalPresentationStyle = .popover + } + hostingViewController.isModalInPresentation = true + self.navigationController?.present(hostingViewController, animated: false, completion: nil) + } + // BWI #7555 END + } override func viewDidAppear(_ animated: Bool) { @@ -317,6 +332,7 @@ class AllChatsViewController: HomeViewController { } AppDelegate.theDelegate().checkAppVersion() + } func presentFederationIntroductionSheet() { diff --git a/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift b/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift index 4476b39a1..e681b815e 100644 --- a/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift +++ b/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift @@ -21,6 +21,10 @@ struct AuthenticationLoginScreen: View { /// This must be manually set back to `false` when the text field finishes editing. @State private var isPasswordFocused = false + // BWI #7555 migration part 3 + @State private var showMigrationView = BWIBuildSettings.shared.BuMXMigrationInfoLevel == 3 + // BWI #7555 END + // MARK: Public @ObservedObject var viewModel: AuthenticationLoginViewModel.Context @@ -110,6 +114,13 @@ struct AuthenticationLoginScreen: View { .background(theme.colors.background.ignoresSafeArea()) .alert(item: $viewModel.alertInfo) { $0.alert } .accentColor(theme.colors.accent) + // BWI #7555 migration part 3 + .sheet(isPresented: $showMigrationView) { + MigrationInfoView(username: "", getUserName: nil) + .environmentObject(BWIThemeService.shared) + .interactiveDismissDisabled(true) + } + // BWI #7555 END } /// The header containing a Welcome Back title. @@ -163,6 +174,7 @@ struct AuthenticationLoginScreen: View { Button(action: submit) { Text(getCustomText(text: .submit)) } + .disabled(BWIBuildSettings.shared.BuMXMigrationInfoLevel > 2) // BWI #7555 migration part 3 .buttonStyle(PrimaryActionButtonStyle()) .disabled(!viewModel.viewState.canSubmit) .accessibilityIdentifier("nextButton") diff --git a/bwi/FeatureBanner/FeatureBannerView.swift b/bwi/FeatureBanner/FeatureBannerView.swift index fee450f9a..d419ad088 100644 --- a/bwi/FeatureBanner/FeatureBannerView.swift +++ b/bwi/FeatureBanner/FeatureBannerView.swift @@ -91,11 +91,12 @@ protocol FeatureBannerDelegate { } func didPressShowDetails() { - let migrationInfoView = MigrationInfoView(username: username).environmentObject(BWIThemeService.shared).interactiveDismissDisabled(true) + let migrationInfoView = MigrationInfoView(username: username, getUserName: nil).environmentObject(BWIThemeService.shared).interactiveDismissDisabled(true) let hostingViewController = UIHostingController(rootView: migrationInfoView) if hostingViewController.popoverPresentationController != nil { hostingViewController.modalPresentationStyle = .popover } + hostingViewController.isModalInPresentation = true hostingController.parent?.present(hostingViewController, animated: true, completion: nil) } @@ -155,8 +156,12 @@ struct FeatureBannerView: View { var advertisementText: some View { VStack { VStack { - if BWIBuildSettings.shared.BuMXMigrationInfoLevel == 1 { - Text(BWIL10n.bwiMobileDialogM1BannerText) + + if BWIBuildSettings.shared.isManagedViaMDM { + Text(BWIL10n.bwiMobileDialogMdmBannerTextPrefix) + + Text(BWIL10n.bwiMobileDialogMdmBannerTextBold) + .bold() + } else if BWIBuildSettings.shared.BuMXMigrationInfoLevel == 1 { + Text(BWIL10n.bwiMobileDialogM1BannerTextPrefix) + Text(BWIL10n.bwiMobileDialogM1BannerTextBold) .bold() } else { diff --git a/bwi/FeatureBanner/MigrationInfoView.swift b/bwi/FeatureBanner/MigrationInfoView.swift index 62e6e2b0b..ed6c69e6c 100644 --- a/bwi/FeatureBanner/MigrationInfoView.swift +++ b/bwi/FeatureBanner/MigrationInfoView.swift @@ -23,15 +23,24 @@ struct MigrationInfoView: View { @State private var selectedTab = 1 @EnvironmentObject var themeService: BWIThemeService @State var redrawKey = UUID() + @State var migrationFinished = SharedKeychain.load(account: "migration_finished") == "true" let username: String + let getUserName: (() -> String?)? var body: some View { TabView(selection: $selectedTab) { - MigrationInfoViewOne() - .tag(1) - MigrationInfoViewTwo(username: username) - .tag(2) + if migrationFinished { + MigrationSuccessView() + .tag(1) + } else { + MigrationInfoViewOne() + .tag(1) + if !username.isEmpty || getUserName != nil { + MigrationInfoViewTwo(username: username, getUserName: getUserName) + .tag(2) + } + } } .id(redrawKey) .tabViewStyle(.page) @@ -58,7 +67,8 @@ struct MigrationInfoView: View { .foregroundColor(Color(themeService.theme.colors.tertiaryContent)) .padding(20) } - .accessibilityLabel(BWIL10n.bwiA11yCloseButton) + .accessibilityLabel(BWIL10n.bwiAllCommonClose) + .isHidden(BWIBuildSettings.shared.BuMXMigrationInfoLevel > 2) } Spacer() HStack() { @@ -90,6 +100,18 @@ struct MigrationInfoView: View { } .opacity(selectedTab == 2 ? 0 : 1) } + .isHidden(migrationFinished || BWIBuildSettings.shared.isManagedViaMDM) + + if BWIBuildSettings.shared.isManagedViaMDM && BWIBuildSettings.shared.BuMXMigrationInfoLevel < 3 { + Button { + dismissView() + } label: { + Text(BWIL10n.bwiAllCommonGotIt) + .foregroundColor(Color(ThemeService.shared().theme.backgroundColor)) + } + .buttonStyle(PrimaryActionButtonStyle()) + .padding() + } } } .onAppear { @@ -149,10 +171,25 @@ struct MigrationInfoViewOne: View { .lineLimit(nil) .fixedSize(horizontal: false, vertical: true) .padding(.bottom, 20) - - Text(BWIL10n.bwiMobileDialogM1MoreText1) + - Text(BWIL10n.bwiMobileDialogM1MoreText2).bold() + - Text(BWIL10n.bwiMobileDialogM1MoreText3) + if BWIBuildSettings.shared.isManagedViaMDM { + if BWIBuildSettings.shared.BuMXMigrationInfoLevel == 3 { + Text(BWIL10n.bwiMobileDialogM3MoreText1) + + Text(BWIL10n.bwiMobileDialogM1MoreText2).bold() + + Text(BWIL10n.bwiMobileDialogM1MoreText3) + } else { + Text(BWIL10n.bwiMobileDialogM1MoreText3) + } + } else { + if BWIBuildSettings.shared.BuMXMigrationInfoLevel < 3 { + Text(BWIL10n.bwiMobileDialogM1MoreText1) + + Text(BWIL10n.bwiMobileDialogM1MoreText2).bold() + + Text(BWIL10n.bwiMobileDialogM1MoreText3) + } else { + Text(BWIL10n.bwiMobileDialogM3MoreText1) + + Text(BWIL10n.bwiMobileDialogM1MoreText2).bold() + + Text(BWIL10n.bwiMobileDialogM1MoreText3) + } + } VStack(alignment: .leading) { HStack(alignment: .top) { Text("•") @@ -178,15 +215,29 @@ struct MigrationInfoViewOne: View { .fixedSize(horizontal: false, vertical: true) .padding(EdgeInsets(top: 10, leading: 30, bottom: 0, trailing: 30)) .accessibilityElement(children: .ignore) - .accessibilityLabel( - BWIL10n.bwiMobileDialogMMoreTitle + ". " + + .accessibilityLabel(accessibilityLabel) + } + + var accessibilityLabel: String { + if BWIBuildSettings.shared.BuMXMigrationInfoLevel < 3 && !BWIBuildSettings.shared.isManagedViaMDM { + return BWIL10n.bwiMobileDialogMMoreTitle + ". " + BWIL10n.bwiMobileDialogM1MoreText1 + BWIL10n.bwiMobileDialogM1MoreText2 + BWIL10n.bwiMobileDialogM1MoreText3 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet1 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet2 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet3 + ", " + - BWIL10n.bwiMobileDialogM1MoreTextBullet4) + BWIL10n.bwiMobileDialogM1MoreTextBullet4 + } else { + return BWIL10n.bwiMobileDialogMMoreTitle + ". " + + BWIL10n.bwiMobileDialogM3MoreText1 + + BWIL10n.bwiMobileDialogM1MoreText2 + + BWIL10n.bwiMobileDialogM1MoreText3 + ", " + + BWIL10n.bwiMobileDialogM1MoreTextBullet1 + ", " + + BWIL10n.bwiMobileDialogM1MoreTextBullet2 + ", " + + BWIL10n.bwiMobileDialogM1MoreTextBullet3 + ", " + + BWIL10n.bwiMobileDialogM1MoreTextBullet4 + } } } @@ -194,6 +245,8 @@ struct MigrationInfoViewOne: View { // MARK: Migraion Info View two struct MigrationInfoViewTwo: View { let username: String + let getUserName: (() -> String?)? + @State var lazyLoadedUserName: String? = nil @State var showSuccessToast: Bool = false @EnvironmentObject var themeService: BWIThemeService @@ -228,7 +281,9 @@ struct MigrationInfoViewTwo: View { } .frame(minHeight: geo.size.height) .frame(width: geo.size.width) - + .onAppear { + lazyLoadedUserName = username.isEmpty ? getUserName?() : username + } } .frame(width: geo.size.width, height: geo.size.height) } @@ -246,7 +301,7 @@ struct MigrationInfoViewTwo: View { HStack() { Text("1.") Button(action: { - UIPasteboard.general.string = username + UIPasteboard.general.string = lazyLoadedUserName ?? "" guard !showSuccessToast else { return } withAnimation { showSuccessToast = true @@ -311,3 +366,48 @@ struct MigrationInfoViewTwo: View { .transition(.opacity) } } + + +// MARK: Migraion Success View +struct MigrationSuccessView: View { + @EnvironmentObject var themeService: BWIThemeService + + var body: some View { + GeometryReader { geo in + ScrollView(.vertical) { + VStack(alignment: .center, spacing: 20) { + VStack() { + Image("bumx_logo") + .resizable() + .frame(width: 200, height: 200) + .clipShape(.rect(cornerRadius: 36)) + .overlay( + RoundedRectangle(cornerRadius: 36) + .stroke(.gray, lineWidth: 0.4) + ) + .accessibilityHidden(true) + } + .frame(height: geo.size.height * 0.35, alignment: .center) + .padding(EdgeInsets(top: 56, leading: 30, bottom: 10, trailing: 30)) + + VStack { + Text(BWIL10n.bwiMobileDialogM3SuccessChangeTitle) + .font(.title) + .bold() + .padding(.bottom, 20) + + Text(BWIL10n.bwiMobileDialogM3SuccessText) + .fixedSize(horizontal: false, vertical: true) + .padding(EdgeInsets(top: 10, leading: 30, bottom: 0, trailing: 30)) + } + + Spacer() + } + .frame(minHeight: geo.size.height) + .frame(width: geo.size.width) + + } + .frame(width: geo.size.width, height: geo.size.height) + } + } +} diff --git a/bwi/MigrationAssistent/MigrationAssistant.swift b/bwi/MigrationAssistent/MigrationAssistant.swift index f55434174..c56ba37ef 100644 --- a/bwi/MigrationAssistent/MigrationAssistant.swift +++ b/bwi/MigrationAssistent/MigrationAssistant.swift @@ -43,4 +43,10 @@ import MatrixSDK return returnValue } + + // BWI #7555 migration part 3 + func loadFinishedFlag() -> String? { + return SharedKeychain.load(account: "migration_finished") + } + // BWI #7555 END } diff --git a/project.yml b/project.yml index be1a146e0..7c436d67b 100644 --- a/project.yml +++ b/project.yml @@ -77,3 +77,8 @@ packages: SwiftJWT: url: https://github.com/Kitura/Swift-JWT version: 4.0.2 + +settings: + base: + SWIFT_ENABLE_EXPLICIT_MODULES: NO + _EXPERIMENTAL_SWIFT_EXPLICIT_MODULES: NO