From 5960f2e3dc61265cd0bd8b46a74cc7a7d1d7665e Mon Sep 17 00:00:00 2001 From: Craig Reyenga <craig.reyenga@gmail.com> Date: Tue, 11 Mar 2025 14:34:09 -0400 Subject: [PATCH 1/5] Move black theme option to settings screen. --- Resources/en.lproj/Localizable.strings | 2 -- .../iOS/Settings.bundle/Root.inApp.plist | 2 -- .../MediaMoreOptionsActionSheet.swift | 3 +- .../Subviews/MediaPlayerActionSheet.swift | 5 --- .../Controller/SettingsController.swift | 3 -- .../Settings/Model/ActionSheetSpecifier.swift | 30 +--------------- Sources/Settings/Model/SettingsSection.swift | 34 +++++-------------- 7 files changed, 10 insertions(+), 69 deletions(-) diff --git a/Resources/en.lproj/Localizable.strings b/Resources/en.lproj/Localizable.strings index 05401c5a2..dababc2b0 100644 --- a/Resources/en.lproj/Localizable.strings +++ b/Resources/en.lproj/Localizable.strings @@ -549,8 +549,6 @@ "FEEDBACK_EMAIL_NOT_POSSIBLE_TITLE" = "Mail account not configured"; "FEEDBACK_EMAIL_NOT_POSSIBLE_LONG" = "Contact us through %@ from another device."; -"SETTINGS_THEME_BLACK" = "Use black background on dark mode"; -"SETTINGS_THEME_BLACK_SUBTITLE" = "Improves battery life on devices with an OLED screen"; "SETTINGS_RESET_TITLE" = "Reset the settings"; "SETTINGS_RESET_MESSAGE" = "Do you want to reset all the settings to their default values?"; diff --git a/Resources/iOS/Settings.bundle/Root.inApp.plist b/Resources/iOS/Settings.bundle/Root.inApp.plist index 2172d720b..8ba36216e 100644 --- a/Resources/iOS/Settings.bundle/Root.inApp.plist +++ b/Resources/iOS/Settings.bundle/Root.inApp.plist @@ -42,14 +42,12 @@ <string>SETTINGS_THEME_BRIGHT</string> <string>SETTINGS_THEME_DARK</string> <string>SETTINGS_THEME_SYSTEM</string> - <string>SETTINGS_THEME_BLACK</string> </array> <key>Values</key> <array> <integer>0</integer> <integer>1</integer> <integer>2</integer> - <string>3</string> </array> </dict> <dict> diff --git a/Sources/Playback/Player/VideoPlayer-iOS/MediaMoreOptionsActionSheet.swift b/Sources/Playback/Player/VideoPlayer-iOS/MediaMoreOptionsActionSheet.swift index 0e587c057..9ab18d603 100644 --- a/Sources/Playback/Player/VideoPlayer-iOS/MediaMoreOptionsActionSheet.swift +++ b/Sources/Playback/Player/VideoPlayer-iOS/MediaMoreOptionsActionSheet.swift @@ -486,8 +486,7 @@ extension MediaMoreOptionsActionSheet: MediaPlayerActionSheetDataSource { } // Do not display these options in the action sheet. - if $0 == .addBookmarks || $0 == .blackBackground || - $0 == .playNextItem || $0 == .playlistPlayNextItem { + if [ .addBookmarks, .playNextItem, .playlistPlayNextItem ].contains($0) { return } diff --git a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaPlayerActionSheet.swift b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaPlayerActionSheet.swift index 9783bb165..21d043f20 100644 --- a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaPlayerActionSheet.swift +++ b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaPlayerActionSheet.swift @@ -22,7 +22,6 @@ enum ActionSheetCellIdentifier: String, CustomStringConvertible, CaseIterable { case addBookmarks case abRepeat case interfaceLock - case blackBackground case playNextItem case playlistPlayNextItem @@ -48,8 +47,6 @@ enum ActionSheetCellIdentifier: String, CustomStringConvertible, CaseIterable { return NSLocalizedString("REPEAT_MODE", comment: "") case .abRepeat: return NSLocalizedString("AB_LOOP", comment: "") - case .blackBackground: - return NSLocalizedString("SETTINGS_THEME_BLACK", comment: "") case .playNextItem: return NSLocalizedString("SETTINGS_PLAY_ALL", comment: "") case .playlistPlayNextItem: @@ -73,8 +70,6 @@ enum ActionSheetCellIdentifier: String, CustomStringConvertible, CaseIterable { return NSLocalizedString("BOOKMARKS_HINT", comment: "") case .interfaceLock: return NSLocalizedString("INTERFACE_LOCK_HINT", comment: "") - case .blackBackground: - return NSLocalizedString("SETTINGS_THEME_BLACK_SUBTITLE", comment: "") case .playNextItem: return NSLocalizedString("SETTINGS_PLAY_ALL_HINT", comment: "") case .playlistPlayNextItem: diff --git a/Sources/Settings/Controller/SettingsController.swift b/Sources/Settings/Controller/SettingsController.swift index bd9aa32be..ae55ad1c3 100644 --- a/Sources/Settings/Controller/SettingsController.swift +++ b/Sources/Settings/Controller/SettingsController.swift @@ -509,9 +509,6 @@ extension SettingsController { extension SettingsController: ActionSheetSpecifierDelegate { func actionSheetSpecifierHandleToggleSwitch(for cell: ActionSheetCell, state: Bool) { switch cell.identifier { - case .blackBackground: - userDefaults.setValue(state, forKey: kVLCSettingAppThemeBlack) - PresentationTheme.themeDidUpdate() case .playNextItem: userDefaults.setValue(state, forKey: kVLCAutomaticallyPlayNextItem) case .playlistPlayNextItem: diff --git a/Sources/Settings/Model/ActionSheetSpecifier.swift b/Sources/Settings/Model/ActionSheetSpecifier.swift index ed4e4db9f..e2fd01020 100644 --- a/Sources/Settings/Model/ActionSheetSpecifier.swift +++ b/Sources/Settings/Model/ActionSheetSpecifier.swift @@ -60,12 +60,6 @@ extension ActionSheetSpecifier: ActionSheetDelegate { return } - guard preferenceKey != kVLCSettingAppTheme || - (!PresentationTheme.current.isDark || indexPath.row != numberOfRows() - 1) else { - // Disable the selection for the black background option cell in the appearance action sheet - return - } - guard preferenceKey != kVLCAutomaticallyPlayNextItem else { // Disable the selection for the automatically play next item options return @@ -106,15 +100,6 @@ extension ActionSheetSpecifier: ActionSheetDataSource { return 0 } - if preferenceKey == kVLCSettingAppTheme { - let isThemeDark: Bool = PresentationTheme.current.isDark - if #available(iOS 13, *) { - return isThemeDark ? rowCount : rowCount - 1 - } else { - return isThemeDark ? rowCount - 1 : rowCount - 2 - } - } - return rowCount } @@ -128,20 +113,7 @@ extension ActionSheetSpecifier: ActionSheetDataSource { return UICollectionViewCell() } - if preferenceKey == kVLCSettingAppTheme && - PresentationTheme.current.isDark && indexPath.row == numberOfRows() - 1 { - // Update the black background option cell - cell.setAccessoryType(to: .toggleSwitch) - cell.setToggleSwitch(state: UserDefaults.standard.bool(forKey: kVLCSettingAppThemeBlack)) - cell.name.text = settingsBundle.localizedString(forKey: "SETTINGS_THEME_BLACK", value: "", table: "Root") - let cellIdentifier = ActionSheetCellIdentifier.blackBackground - cell.identifier = cellIdentifier - cell.name.accessibilityLabel = cellIdentifier.description - cell.name.accessibilityHint = cellIdentifier.accessibilityHint - cell.delegate = self - - return cell - } else if preferenceKey == kVLCAutomaticallyPlayNextItem { + if preferenceKey == kVLCAutomaticallyPlayNextItem { cell.setAccessoryType(to: .toggleSwitch) let isFirstRow: Bool = indexPath.row == 0 diff --git a/Sources/Settings/Model/SettingsSection.swift b/Sources/Settings/Model/SettingsSection.swift index a7b6ffbe5..5b40cb6f4 100644 --- a/Sources/Settings/Model/SettingsSection.swift +++ b/Sources/Settings/Model/SettingsSection.swift @@ -44,7 +44,7 @@ struct SettingsItem: Equatable { self.isTitleEmphasized = isTitleEmphasized } - static func toggle(title: String, subtitle: String?, preferenceKey: String) -> Self { + static func toggle(title: String, subtitle: String? = nil, preferenceKey: String) -> Self { return Self(title: title, subtitle: subtitle, action: .toggle(Toggle(preferenceKey: preferenceKey))) } @@ -176,10 +176,17 @@ enum MainOptions { action: .showActionSheet(title: "SETTINGS_DARKTHEME", preferenceKey: k, hasInfo: false)) } + static var blackTheme: SettingsItem { + .toggle(title: "SETTINGS_THEME_BLACK", + subtitle: "SETTINGS_THEME_BLACK_SUBTITLE", + preferenceKey: kVLCSettingAppThemeBlack) + } + static func section() -> SettingsSection? { .init(title: nil, items: [ privacy, appearance, + blackTheme ]) } } @@ -217,7 +224,6 @@ enum GenericOptions { static var playVideoInFullScreen: SettingsItem { .toggle(title: "SETTINGS_VIDEO_FULLSCREEN", - subtitle: nil, preferenceKey: kVLCSettingVideoFullscreenPlayback) } @@ -237,19 +243,16 @@ enum GenericOptions { static var enableTextScrollingInMediaList: SettingsItem { .toggle(title: "SETTINGS_ENABLE_MEDIA_CELL_TEXT_SCROLLING", - subtitle: nil, preferenceKey: kVLCSettingEnableMediaCellTextScrolling) } static var rememberPlayerState: SettingsItem { .toggle(title: "SETTINGS_REMEMBER_PLAYER_STATE", - subtitle: nil, preferenceKey: kVLCPlayerShouldRememberState) } static var restoreLastPlayedMedia: SettingsItem { .toggle(title: "SETTINGS_RESTORE_LAST_PLAYED_MEDIA", - subtitle: nil, preferenceKey: kVLCRestoreLastPlayedMedia) } @@ -323,43 +326,36 @@ enum PrivacyOptions { enum GestureControlOptions { static var swipeUpDownForVolume: SettingsItem { .toggle(title: "SETTINGS_GESTURES_VOLUME", - subtitle: nil, preferenceKey: kVLCSettingVolumeGesture) } static var twoFingerTap: SettingsItem { .toggle(title: "SETTINGS_GESTURES_PLAYPAUSE", - subtitle: nil, preferenceKey: kVLCSettingPlayPauseGesture) } static var swipeUpDownForBrightness: SettingsItem { .toggle(title: "SETTINGS_GESTURES_BRIGHTNESS", - subtitle: nil, preferenceKey: kVLCSettingBrightnessGesture) } static var swipeRightLeftToSeek: SettingsItem { .toggle(title: "SETTINGS_GESTURES_SEEK", - subtitle: nil, preferenceKey: kVLCSettingSeekGesture) } static var pinchToClose: SettingsItem { .toggle(title: "SETTINGS_GESTURES_CLOSE", - subtitle: nil, preferenceKey: kVLCSettingCloseGesture) } static var forwardBackwardEqual: SettingsItem { .toggle(title: "SETTINGS_GESTURES_FORWARD_BACKWARD_EQUAL", - subtitle: nil, preferenceKey: kVLCSettingPlaybackForwardBackwardEqual) } static var tapSwipeEqual: SettingsItem { .toggle(title: "SETTINGS_GESTURES_TAP_SWIPE_EQUAL", - subtitle: nil, preferenceKey: kVLCSettingPlaybackTapSwipeEqual) } @@ -393,7 +389,6 @@ enum GestureControlOptions { static var longTouchToSpeedUp: SettingsItem { .toggle(title: "SETINGS_LONG_TOUCH_SPEED_UP", - subtitle: nil, preferenceKey: kVLCSettingPlaybackLongTouchSpeedUp) } @@ -492,13 +487,11 @@ enum VideoOptions { static var rememberPlayerBrightness: SettingsItem { .toggle(title: "SETTINGS_REMEMBER_PLAYER_BRIGHTNESS", - subtitle: nil, preferenceKey: kVLCPlayerShouldRememberBrightness) } static var lockRotation: SettingsItem { .toggle(title: "SETTINGS_LOCK_ROTATION", - subtitle: nil, preferenceKey: kVLCSettingRotationLock) } @@ -538,7 +531,6 @@ enum SubtitlesOptions { static var useBoldFont: SettingsItem { .toggle(title: "SETTINGS_SUBTITLES_BOLDFONT", - subtitle: nil, preferenceKey: kVLCSettingSubtitlesBoldFont) } @@ -610,7 +602,6 @@ enum AudioOptions { static var audioPlaybackInBackground: SettingsItem { .toggle(title: "SETTINGS_BACKGROUND_AUDIO", - subtitle: nil, preferenceKey: kVLCSettingContinueAudioInBackgroundKey) } @@ -635,31 +626,26 @@ enum MediaLibraryOptions { static var optimiseItemNamesForDisplay: SettingsItem { .toggle(title: "SETTINGS_DECRAPIFY", - subtitle: nil, preferenceKey: kVLCSettingsDecrapifyTitles) } static var disableGrouping: SettingsItem { .toggle(title: "SETTINGS_DISABLE_GROUPING", - subtitle: nil, preferenceKey: kVLCSettingsDisableGrouping) } static var showVideoThumbnails: SettingsItem { .toggle(title: "SETTINGS_SHOW_THUMBNAILS", - subtitle: nil, preferenceKey: kVLCSettingShowThumbnails) } static var showAudioArtworks: SettingsItem { .toggle(title: "SETTINGS_SHOW_ARTWORKS", - subtitle: nil, preferenceKey: kVLCSettingShowArtworks) } static var includeMediaLibInDeviceBackup: SettingsItem { .toggle(title: "SETTINGS_BACKUP_MEDIA_LIBRARY", - subtitle: nil, preferenceKey: kVLCSettingBackupMediaLibrary) } @@ -698,7 +684,6 @@ enum NetworkOptions { static var ipv6SupportForWiFiSharing: SettingsItem { .toggle(title: "SETTINGS_WIFISHARING_IPv6", - subtitle: nil, preferenceKey: kVLCSettingWiFiSharingIPv6) } @@ -710,7 +695,6 @@ enum NetworkOptions { static var rtspctp: SettingsItem { .toggle(title: "SETTINGS_RTSP_TCP", - subtitle: nil, preferenceKey: kVLCSettingNetworkRTSPTCP) } @@ -736,7 +720,6 @@ enum Accessibility { static var pauseWhenShowingControls: SettingsItem { .toggle(title: "SETTINGS_PAUSE_WHEN_SHOWING_CONTROLS", - subtitle: nil, preferenceKey: kVLCSettingPauseWhenShowingControls) } @@ -753,7 +736,6 @@ enum Accessibility { enum Lab { static var debugLogging: SettingsItem { .toggle(title: "SETTINGS_DEBUG_LOG", - subtitle: nil, preferenceKey: kVLCSaveDebugLogs) } -- GitLab From 448dfaa249c3d881625987b950f5a8f5a0732783 Mon Sep 17 00:00:00 2001 From: Craig Reyenga <craig.reyenga@gmail.com> Date: Tue, 11 Mar 2025 15:07:54 -0400 Subject: [PATCH 2/5] Introduced concept of enabled or disabled settings items. Enforce with black theme option. --- Sources/Settings/Model/SettingsSection.swift | 11 +++++++---- Sources/Settings/View/SettingsCell.swift | 10 ++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Sources/Settings/Model/SettingsSection.swift b/Sources/Settings/Model/SettingsSection.swift index 5b40cb6f4..c0c76f41e 100644 --- a/Sources/Settings/Model/SettingsSection.swift +++ b/Sources/Settings/Model/SettingsSection.swift @@ -23,6 +23,7 @@ struct SettingsItem: Equatable { let title: String let subtitle: String? let action: Action + let isEnabled: Bool let isTitleEmphasized: Bool @available(*, deprecated, message: "access from self.action") @@ -37,15 +38,16 @@ struct SettingsItem: Equatable { } } - init(title: String, subtitle: String?, action: Action, isTitleEmphasized: Bool = false) { + init(title: String, subtitle: String?, action: Action, isEnabled: Bool = true, isTitleEmphasized: Bool = false) { self.title = Localizer.localizedTitle(key: title) self.subtitle = subtitle.flatMap(Localizer.localizedTitle(key:)) self.action = action + self.isEnabled = isEnabled self.isTitleEmphasized = isTitleEmphasized } - static func toggle(title: String, subtitle: String? = nil, preferenceKey: String) -> Self { - return Self(title: title, subtitle: subtitle, action: .toggle(Toggle(preferenceKey: preferenceKey))) + static func toggle(title: String, subtitle: String? = nil, preferenceKey: String, isEnabled: Bool = true) -> Self { + return Self(title: title, subtitle: subtitle, action: .toggle(Toggle(preferenceKey: preferenceKey)), isEnabled: isEnabled) } enum Action: Equatable { @@ -179,7 +181,8 @@ enum MainOptions { static var blackTheme: SettingsItem { .toggle(title: "SETTINGS_THEME_BLACK", subtitle: "SETTINGS_THEME_BLACK_SUBTITLE", - preferenceKey: kVLCSettingAppThemeBlack) + preferenceKey: kVLCSettingAppThemeBlack, + isEnabled: UserDefaults.standard.integer(forKey: kVLCSettingAppTheme) != kVLCSettingAppThemeBright) } static func section() -> SettingsSection? { diff --git a/Sources/Settings/View/SettingsCell.swift b/Sources/Settings/View/SettingsCell.swift index 0f9a69edc..356c0b8c0 100644 --- a/Sources/Settings/View/SettingsCell.swift +++ b/Sources/Settings/View/SettingsCell.swift @@ -30,6 +30,7 @@ class SettingsCell: UITableViewCell { static let marginBottom: CGFloat = 10 static let marginLeading: CGFloat = 20 static let marginTrailing: CGFloat = 70 + static let disabledAlpha: CGFloat = 0.3 } weak var delegate: SettingsCellDelegate? @@ -208,6 +209,15 @@ class SettingsCell: UITableViewCell { } else { activityIndicator.stopAnimating() } + + if settingsItem.isEnabled { + switchControl.isEnabled = true + contentView.alpha = 1 + } else { + switchControl.isEnabled = false + contentView.alpha = Constants.disabledAlpha + } + } } -- GitLab From f5ff51cd73c7f5d3ea49625f0aa05ebe160536ad Mon Sep 17 00:00:00 2001 From: Craig Reyenga <craig.reyenga@gmail.com> Date: Tue, 11 Mar 2025 16:37:00 -0400 Subject: [PATCH 3/5] Fix issues with theme settings updates, especially in the settings screen itself. --- Sources/App/iOS/DefaultsChangeListener.swift | 66 +++++++++++++++++++ Sources/App/iOS/VLCAppCoordinator.m | 3 + .../Controller/SettingsController.swift | 9 ++- .../Settings/Model/ActionSheetSpecifier.swift | 4 -- Sources/Settings/View/SettingsCell.swift | 4 ++ Sources/UI Elements/PresentationTheme.swift | 48 ++++++++------ VLC.xcodeproj/project.pbxproj | 6 ++ 7 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 Sources/App/iOS/DefaultsChangeListener.swift diff --git a/Sources/App/iOS/DefaultsChangeListener.swift b/Sources/App/iOS/DefaultsChangeListener.swift new file mode 100644 index 000000000..1abf1fd7a --- /dev/null +++ b/Sources/App/iOS/DefaultsChangeListener.swift @@ -0,0 +1,66 @@ +/***************************************************************************** + * DefaultsChangeListener.swift + * VLC for iOS + ***************************************************************************** + * Copyright (c) 2025 VideoLAN. All rights reserved. + * $Id$ + * + * Authors: Craig Reyenga <craig.reyenga # gmail.com> + * + * Refer to the COPYING file of the official project for license. + *****************************************************************************/ + +/// Emits notifications or performs other actions based on updates to user defaults. +@objc(VLCDefaultsChangeListener) +final class DefaultsChangeListener: NSObject { + private var appTheme: ChangeManager<Int> + private var appThemeBlack: ChangeManager<Int> + + override init() { + appTheme = ChangeManager(value: Self.readAppTheme(), action: { _ in + PresentationTheme.themeDidUpdate() + }) + + appThemeBlack = ChangeManager(value: Self.readAppThemeBlack(), action: { _ in + PresentationTheme.themeDidUpdate() + }) + + super.init() + + let notificationCenter = NotificationCenter.default + notificationCenter.addObserver(self, + selector: #selector(userDefaultsUpdated), + name: UserDefaults.didChangeNotification, + object: nil) + } + + @objc private func userDefaultsUpdated() { + appTheme.update(Self.readAppTheme()) + appThemeBlack.update(Self.readAppThemeBlack()) + } + + static func readAppTheme() -> Int { + return UserDefaults.standard.integer(forKey: kVLCSettingAppTheme) + } + + static func readAppThemeBlack() -> Int { + return UserDefaults.standard.integer(forKey: kVLCSettingAppThemeBlack) + } +} + +/// Executes an action when the underlying value has changed. +fileprivate final class ChangeManager<T: Equatable> { + var value: T + let action: (T) -> Void + + init(value: T, action: @escaping (T) -> Void) { + self.value = value + self.action = action + } + + func update(_ newValue: T) { + guard value != newValue else { return } + value = newValue + action(newValue) + } +} diff --git a/Sources/App/iOS/VLCAppCoordinator.m b/Sources/App/iOS/VLCAppCoordinator.m index 0b0b8c2d8..14c69ed31 100644 --- a/Sources/App/iOS/VLCAppCoordinator.m +++ b/Sources/App/iOS/VLCAppCoordinator.m @@ -28,6 +28,7 @@ VLCRemoteControlService *_remoteControlService; UIWindow *_externalWindow; VLCStripeController *_stripeController; + VLCDefaultsChangeListener *_defaultsChangeListener; #if TARGET_OS_IOS VLCRendererDiscovererManager *_rendererDiscovererManager; @@ -54,6 +55,8 @@ { self = [super init]; if (self) { + _defaultsChangeListener = [[VLCDefaultsChangeListener alloc] init]; + dispatch_async(dispatch_get_main_queue(), ^{ [VLCLibrary setSharedEventsConfiguration:[VLCEventsLegacyConfiguration new]]; [self initializeServices]; diff --git a/Sources/Settings/Controller/SettingsController.swift b/Sources/Settings/Controller/SettingsController.swift index ae55ad1c3..ca7965c63 100644 --- a/Sources/Settings/Controller/SettingsController.swift +++ b/Sources/Settings/Controller/SettingsController.swift @@ -169,7 +169,14 @@ class SettingsController: UITableViewController { #if os(iOS) setNeedsStatusBarAppearanceUpdate() #endif - reloadSettingsSections() // When theme changes hide the black theme section if needed + + tableView.visibleCells.forEach { cell in + guard let cell = cell as? SettingsCell else { return } + + cell.themeChanged() + } + + reloadSettingsSections() } @objc private func miniPlayerIsShown() { diff --git a/Sources/Settings/Model/ActionSheetSpecifier.swift b/Sources/Settings/Model/ActionSheetSpecifier.swift index e2fd01020..1b6c16b77 100644 --- a/Sources/Settings/Model/ActionSheetSpecifier.swift +++ b/Sources/Settings/Model/ActionSheetSpecifier.swift @@ -67,10 +67,6 @@ extension ActionSheetSpecifier: ActionSheetDelegate { userDefaults.set(settingSpecifier?.specifier[indexPath.row].value, forKey: preferenceKey) - if preferenceKey == kVLCSettingAppTheme { - PresentationTheme.themeDidUpdate() - } - #if os(iOS) NotificationFeedbackGenerator().success() #endif diff --git a/Sources/Settings/View/SettingsCell.swift b/Sources/Settings/View/SettingsCell.swift index 356c0b8c0..7d83ea10d 100644 --- a/Sources/Settings/View/SettingsCell.swift +++ b/Sources/Settings/View/SettingsCell.swift @@ -231,6 +231,10 @@ class SettingsCell: UITableViewCell { fatalError("init(coder:) has not been implemented") } + func themeChanged() { + setupTheme() + } + override func prepareForReuse() { super.prepareForReuse() backgroundColor = .clear // Required to prevent theme mismatch during setupTheme diff --git a/Sources/UI Elements/PresentationTheme.swift b/Sources/UI Elements/PresentationTheme.swift index 10585824d..7fa2e8c33 100644 --- a/Sources/UI Elements/PresentationTheme.swift +++ b/Sources/UI Elements/PresentationTheme.swift @@ -43,27 +43,27 @@ extension Notification.Name { let thumbnailBackgroundColor: UIColor init(isDark: Bool, - name: String, - statusBarStyle: UIStatusBarStyle, - navigationbarColor: UIColor, - navigationbarTextColor: UIColor, - background: UIColor, - cellBackgroundA: UIColor, - cellBackgroundB: UIColor, - cellDetailTextColor: UIColor, - cellTextColor: UIColor, - lightTextColor: UIColor, - sectionHeaderTextColor: UIColor, - separatorColor: UIColor, - mediaCategorySeparatorColor: UIColor, - tabBarColor: UIColor, - orangeUI: UIColor, - orangeDarkAccent: UIColor, - toolBarStyle: UIBarStyle, - blurStyle: UIBlurEffect.Style, - textfieldBorderColor: UIColor, - textfieldPlaceholderColor: UIColor, - thumbnailBackgroundColor: UIColor) { + name: String, + statusBarStyle: UIStatusBarStyle, + navigationbarColor: UIColor, + navigationbarTextColor: UIColor, + background: UIColor, + cellBackgroundA: UIColor, + cellBackgroundB: UIColor, + cellDetailTextColor: UIColor, + cellTextColor: UIColor, + lightTextColor: UIColor, + sectionHeaderTextColor: UIColor, + separatorColor: UIColor, + mediaCategorySeparatorColor: UIColor, + tabBarColor: UIColor, + orangeUI: UIColor, + orangeDarkAccent: UIColor, + toolBarStyle: UIBarStyle, + blurStyle: UIBlurEffect.Style, + textfieldBorderColor: UIColor, + textfieldPlaceholderColor: UIColor, + thumbnailBackgroundColor: UIColor) { self.isDark = isDark self.name = name self.statusBarStyle = statusBarStyle @@ -89,6 +89,8 @@ extension Notification.Name { } } +// MARK: - Typography + @objcMembers class Typography: NSObject { let tableHeaderFont: UIFont @@ -106,6 +108,8 @@ enum PresentationThemeType: Int { case auto } +// MARK: - PresentationTheme + @objcMembers class PresentationTheme: NSObject { static let brightTheme = PresentationTheme(colors: brightPalette) @@ -182,6 +186,8 @@ enum PresentationThemeType: Int { let font = defaultFont } +// MARK: - UIColor + @objc extension UIColor { convenience init(_ rgbValue: UInt32, _ alpha: CGFloat = 1.0) { diff --git a/VLC.xcodeproj/project.pbxproj b/VLC.xcodeproj/project.pbxproj index 4f743280f..ee52f64ec 100644 --- a/VLC.xcodeproj/project.pbxproj +++ b/VLC.xcodeproj/project.pbxproj @@ -96,6 +96,8 @@ 444E5BFA24C6081B0003B69C /* PasscodeLockController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E5BF924C6081A0003B69C /* PasscodeLockController.swift */; }; 444E5C0024C719480003B69C /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E5BFF24C719480003B69C /* AboutController.swift */; }; 44B5822024E434FD001A2583 /* MediaGridCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44B5821F24E434FD001A2583 /* MediaGridCollectionCell.swift */; }; + 44BA0BD62D80CF4D004CF52E /* DefaultsChangeListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44BA0BD52D80CF20004CF52E /* DefaultsChangeListener.swift */; }; + 44BA0BD72D80CF4D004CF52E /* DefaultsChangeListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44BA0BD52D80CF20004CF52E /* DefaultsChangeListener.swift */; }; 44C8BBA324AF2B5C003F8940 /* FeedbackGenerators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44C8BBA224AF2B5C003F8940 /* FeedbackGenerators.swift */; }; 44C8BBAE24AF36F4003F8940 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44C8BBA624AF36F4003F8940 /* SettingsController.swift */; }; 44C8BBAF24AF36F4003F8940 /* SettingsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44C8BBA824AF36F4003F8940 /* SettingsSection.swift */; }; @@ -1013,6 +1015,7 @@ 444E5BF924C6081A0003B69C /* PasscodeLockController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeLockController.swift; sourceTree = "<group>"; }; 444E5BFF24C719480003B69C /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; }; 44B5821F24E434FD001A2583 /* MediaGridCollectionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaGridCollectionCell.swift; sourceTree = "<group>"; }; + 44BA0BD52D80CF20004CF52E /* DefaultsChangeListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultsChangeListener.swift; sourceTree = "<group>"; }; 44C8BBA224AF2B5C003F8940 /* FeedbackGenerators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbackGenerators.swift; sourceTree = "<group>"; }; 44C8BBA624AF36F4003F8940 /* SettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; }; 44C8BBA824AF36F4003F8940 /* SettingsSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSection.swift; sourceTree = "<group>"; }; @@ -3100,6 +3103,7 @@ 418B144C20179C74000447AA /* TabBarCoordinator.swift */, 8F9108342A67AC76007EB0D5 /* SirikitIntentCoordinator.swift */, 91562F582CAEBC1500D42986 /* PlayMediaIntent.swift */, + 44BA0BD52D80CF20004CF52E /* DefaultsChangeListener.swift */, ); path = iOS; sourceTree = "<group>"; @@ -4444,6 +4448,7 @@ 7D50C6902BBD20DF00B9F1A0 /* VLCLocalNetworkServiceNetService.m in Sources */, 7D274D582D2AE81C00ADDC41 /* LongPressPlaybackSpeedView.swift in Sources */, 7D50C6912BBD20DF00B9F1A0 /* VLCPlexWebAPI.m in Sources */, + 44BA0BD72D80CF4D004CF52E /* DefaultsChangeListener.swift in Sources */, 7D50C6922BBD20DF00B9F1A0 /* VLCDonationNagScreenViewController.m in Sources */, 7D50C6932BBD20DF00B9F1A0 /* ButtonBarView.swift in Sources */, 7D50C6942BBD20DF00B9F1A0 /* MediaPlayerActionSheet.swift in Sources */, @@ -4641,6 +4646,7 @@ DD3EFF411BDEBCE500B68579 /* VLCNetworkServerBrowserSharedLibrary.m in Sources */, 7DC1865A2A0BB0C3009E84E1 /* UIImage+Gradient.swift in Sources */, 8D15A1F022B0FB9300CFA758 /* QueueViewController.swift in Sources */, + 44BA0BD62D80CF4D004CF52E /* DefaultsChangeListener.swift in Sources */, D96C9EBC28105F5B005F13BB /* AlbumHeader.swift in Sources */, 418E88412110E51B00DDA6A7 /* URLHandler.swift in Sources */, 8DE187812105DAB100A091D2 /* VideoViewController.swift in Sources */, -- GitLab From 40a2608bf00e702c786b410081f94b8a3bc04e06 Mon Sep 17 00:00:00 2001 From: Craig Reyenga <craig.reyenga@gmail.com> Date: Wed, 12 Mar 2025 09:45:55 -0400 Subject: [PATCH 4/5] Move disableGrouping into DefaultsChangeListener. --- Sources/App/iOS/DefaultsChangeListener.swift | 17 ++++++++++++++--- .../Controller/SettingsController.swift | 8 -------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Sources/App/iOS/DefaultsChangeListener.swift b/Sources/App/iOS/DefaultsChangeListener.swift index 1abf1fd7a..cc0260f40 100644 --- a/Sources/App/iOS/DefaultsChangeListener.swift +++ b/Sources/App/iOS/DefaultsChangeListener.swift @@ -13,10 +13,13 @@ /// Emits notifications or performs other actions based on updates to user defaults. @objc(VLCDefaultsChangeListener) final class DefaultsChangeListener: NSObject { - private var appTheme: ChangeManager<Int> - private var appThemeBlack: ChangeManager<Int> + private let appTheme: ChangeManager<Int> + private let appThemeBlack: ChangeManager<Int> + private let disableGrouping: ChangeManager<Bool> override init() { + let notificationCenter = NotificationCenter.default + appTheme = ChangeManager(value: Self.readAppTheme(), action: { _ in PresentationTheme.themeDidUpdate() }) @@ -25,9 +28,12 @@ final class DefaultsChangeListener: NSObject { PresentationTheme.themeDidUpdate() }) + disableGrouping = ChangeManager(value: Self.readDisableGrouping(), action: { _ in + notificationCenter.post(name: .VLCDisableGroupingDidChangeNotification, object: nil) + }) + super.init() - let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(userDefaultsUpdated), name: UserDefaults.didChangeNotification, @@ -37,6 +43,7 @@ final class DefaultsChangeListener: NSObject { @objc private func userDefaultsUpdated() { appTheme.update(Self.readAppTheme()) appThemeBlack.update(Self.readAppThemeBlack()) + disableGrouping.update(Self.readDisableGrouping()) } static func readAppTheme() -> Int { @@ -46,6 +53,10 @@ final class DefaultsChangeListener: NSObject { static func readAppThemeBlack() -> Int { return UserDefaults.standard.integer(forKey: kVLCSettingAppThemeBlack) } + + static func readDisableGrouping() -> Bool { + return UserDefaults.standard.bool(forKey: kVLCSettingsDisableGrouping) + } } /// Executes an action when the underlying value has changed. diff --git a/Sources/Settings/Controller/SettingsController.swift b/Sources/Settings/Controller/SettingsController.swift index ca7965c63..4435b1f2a 100644 --- a/Sources/Settings/Controller/SettingsController.swift +++ b/Sources/Settings/Controller/SettingsController.swift @@ -443,8 +443,6 @@ extension SettingsController: SettingsCellDelegate { medialibraryHidingLockSwitchOn(state: isOn) case kVLCSettingBackupMediaLibrary: mediaLibraryBackupActivateSwitchOn(state: isOn) - case kVLCSettingsDisableGrouping: - medialibraryDisableGroupingSwitchOn(state: isOn) default: break } @@ -507,12 +505,6 @@ extension SettingsController { } } -extension SettingsController { - func medialibraryDisableGroupingSwitchOn(state _: Bool) { - notificationCenter.post(name: .VLCDisableGroupingDidChangeNotification, object: self) - } -} - extension SettingsController: ActionSheetSpecifierDelegate { func actionSheetSpecifierHandleToggleSwitch(for cell: ActionSheetCell, state: Bool) { switch cell.identifier { -- GitLab From 8d47fde88baecf23d3165257cd11a279342b17c1 Mon Sep 17 00:00:00 2001 From: Craig Reyenga <craig.reyenga@gmail.com> Date: Wed, 12 Mar 2025 10:24:17 -0400 Subject: [PATCH 5/5] Move hideMediaLibrary and excludeFromDeviceBackup into DefaultsChangeListener. --- Sources/App/iOS/DefaultsChangeListener.swift | 35 +++++++++++++++++-- Sources/App/iOS/VLCAppCoordinator.m | 2 ++ .../Controller/SettingsController.swift | 16 --------- Sources/Settings/View/SettingsCell.swift | 1 + 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/Sources/App/iOS/DefaultsChangeListener.swift b/Sources/App/iOS/DefaultsChangeListener.swift index cc0260f40..e4da29960 100644 --- a/Sources/App/iOS/DefaultsChangeListener.swift +++ b/Sources/App/iOS/DefaultsChangeListener.swift @@ -16,6 +16,10 @@ final class DefaultsChangeListener: NSObject { private let appTheme: ChangeManager<Int> private let appThemeBlack: ChangeManager<Int> private let disableGrouping: ChangeManager<Bool> + private let hideMediaLibrary: ChangeManager<Bool> + private let excludeFromDeviceBackup: ChangeManager<Bool> + + @objc var mediaLibraryService: MediaLibraryService? override init() { let notificationCenter = NotificationCenter.default @@ -32,18 +36,32 @@ final class DefaultsChangeListener: NSObject { notificationCenter.post(name: .VLCDisableGroupingDidChangeNotification, object: nil) }) + hideMediaLibrary = ChangeManager(value: Self.readHideMediaLibrary()) + + excludeFromDeviceBackup = ChangeManager(value: Self.readExcludeFromDeviceBackup()) + super.init() notificationCenter.addObserver(self, selector: #selector(userDefaultsUpdated), name: UserDefaults.didChangeNotification, object: nil) + + hideMediaLibrary.action = { [weak self] hide in + self?.mediaLibraryService?.hideMediaLibrary(hide) + } + + excludeFromDeviceBackup.action = { [weak self] exclude in + self?.mediaLibraryService?.excludeFromDeviceBackup(exclude) + } } @objc private func userDefaultsUpdated() { appTheme.update(Self.readAppTheme()) appThemeBlack.update(Self.readAppThemeBlack()) disableGrouping.update(Self.readDisableGrouping()) + hideMediaLibrary.update(Self.readHideMediaLibrary()) + excludeFromDeviceBackup.update(Self.readExcludeFromDeviceBackup()) } static func readAppTheme() -> Int { @@ -57,14 +75,25 @@ final class DefaultsChangeListener: NSObject { static func readDisableGrouping() -> Bool { return UserDefaults.standard.bool(forKey: kVLCSettingsDisableGrouping) } + + static func readHideMediaLibrary() -> Bool { + return UserDefaults.standard.bool(forKey: kVLCSettingHideLibraryInFilesApp) + } + + static func readExcludeFromDeviceBackup() -> Bool { + // The user interface setting is for *including* in a backup, but + // user defaults and the media library refer to *exclusion*. We perform + // the inversion right here to have everything match up. + return !UserDefaults.standard.bool(forKey: kVLCSettingBackupMediaLibrary) + } } /// Executes an action when the underlying value has changed. fileprivate final class ChangeManager<T: Equatable> { var value: T - let action: (T) -> Void + var action: ((T) -> Void)? - init(value: T, action: @escaping (T) -> Void) { + init(value: T, action: ((T) -> Void)? = nil) { self.value = value self.action = action } @@ -72,6 +101,6 @@ fileprivate final class ChangeManager<T: Equatable> { func update(_ newValue: T) { guard value != newValue else { return } value = newValue - action(newValue) + action?(newValue) } } diff --git a/Sources/App/iOS/VLCAppCoordinator.m b/Sources/App/iOS/VLCAppCoordinator.m index 14c69ed31..9a0f8906d 100644 --- a/Sources/App/iOS/VLCAppCoordinator.m +++ b/Sources/App/iOS/VLCAppCoordinator.m @@ -74,6 +74,8 @@ // start the remote control service _remoteControlService = [[VLCRemoteControlService alloc] init]; + + _defaultsChangeListener.mediaLibraryService = self.mediaLibraryService; } - (MediaLibraryService *)mediaLibraryService diff --git a/Sources/Settings/Controller/SettingsController.swift b/Sources/Settings/Controller/SettingsController.swift index 4435b1f2a..287bd6baf 100644 --- a/Sources/Settings/Controller/SettingsController.swift +++ b/Sources/Settings/Controller/SettingsController.swift @@ -439,10 +439,6 @@ extension SettingsController: SettingsCellDelegate { switch preferenceKey { case kVLCSettingPasscodeOnKey: passcodeLockSwitchOn(state: isOn) - case kVLCSettingHideLibraryInFilesApp: - medialibraryHidingLockSwitchOn(state: isOn) - case kVLCSettingBackupMediaLibrary: - mediaLibraryBackupActivateSwitchOn(state: isOn) default: break } @@ -493,18 +489,6 @@ extension SettingsController { } } -extension SettingsController { - func medialibraryHidingLockSwitchOn(state: Bool) { - mediaLibraryService.hideMediaLibrary(state) - } -} - -extension SettingsController { - func mediaLibraryBackupActivateSwitchOn(state: Bool) { - mediaLibraryService.excludeFromDeviceBackup(state) - } -} - extension SettingsController: ActionSheetSpecifierDelegate { func actionSheetSpecifierHandleToggleSwitch(for cell: ActionSheetCell, state: Bool) { switch cell.identifier { diff --git a/Sources/Settings/View/SettingsCell.swift b/Sources/Settings/View/SettingsCell.swift index 7d83ea10d..a529bacec 100644 --- a/Sources/Settings/View/SettingsCell.swift +++ b/Sources/Settings/View/SettingsCell.swift @@ -15,6 +15,7 @@ protocol SettingsCellDelegate: AnyObject { /// Implementations should only perform side effects on /// specific preferences; updating the preference itself /// is handled by the cell. + @available(*, deprecated, message: "Side effects of toggling a setting should be performed by observing changes to userdefaults") func settingsCellDidChangeSwitchState(cell: SettingsCell, preferenceKey: String, isOn: Bool) func settingsCellInfoButtonPressed(cell: SettingsCell, preferenceKey: String) -- GitLab