diff --git a/MedoDelirioBrasilia.xcodeproj/project.pbxproj b/MedoDelirioBrasilia.xcodeproj/project.pbxproj index dced42749..f5b39df56 100644 --- a/MedoDelirioBrasilia.xcodeproj/project.pbxproj +++ b/MedoDelirioBrasilia.xcodeproj/project.pbxproj @@ -273,6 +273,7 @@ D92F096529ED844300CC382F /* View+Marquee.swift in Sources */ = {isa = PBXBuildFile; fileRef = D92F096429ED844300CC382F /* View+Marquee.swift */; }; D9310C262A0BDA90006A3058 /* Jaiminho - E que quero evitar a fadiga.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = D9310C252A0BDA90006A3058 /* Jaiminho - E que quero evitar a fadiga.mp3 */; }; D933251E28835E41004EFACB /* ActivityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D933251D28835E41004EFACB /* ActivityViewController.swift */; }; + D93391F62D3307B100DC2297 /* MainSoundContainerViewModel+SpotlightIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93391F52D3307B000DC2297 /* MainSoundContainerViewModel+SpotlightIndex.swift */; }; D9344323283812AD00ADCD38 /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9344322283812AD00ADCD38 /* Author.swift */; }; D934432728381DEF00ADCD38 /* author_data.json in Resources */ = {isa = PBXBuildFile; fileRef = D934432628381DEF00ADCD38 /* author_data.json */; }; D93443292838230500ADCD38 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93443282838230500ADCD38 /* String+.swift */; }; @@ -1231,7 +1232,7 @@ D9AB63F02A5A063700390778 /* UIDevice+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9FA0CD628508590000F6ADE /* UIDevice+.swift */; }; D9AB63F42A5A085800390778 /* SyncServiceStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AB63F32A5A085800390778 /* SyncServiceStub.swift */; }; D9ABDDB0287411C9007B25EA /* SongHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9ABDDAF287411C9007B25EA /* SongHelpView.swift */; }; - D9ABEEDB2CA1B51800CA024F /* PlayRandomSoundHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9ABEEDA2CA1B50F00CA024F /* PlayRandomSoundHelper.swift */; }; + D9ABEEDB2CA1B51800CA024F /* ExternalActivityHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9ABEEDA2CA1B50F00CA024F /* ExternalActivityHelper.swift */; }; D9ADEB6329861A79001CB004 /* Ai eu acho exagero.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = D9ADEB6229861A79001CB004 /* Ai eu acho exagero.mp3 */; }; D9ADEB6729861BD3001CB004 /* Bem-vindos ao devido processo legal.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = D9ADEB6429861BD2001CB004 /* Bem-vindos ao devido processo legal.mp3 */; }; D9ADEB6829861BD3001CB004 /* Ana Priscila Azevedo (golpista) - Senhores, selva.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = D9ADEB6529861BD2001CB004 /* Ana Priscila Azevedo (golpista) - Senhores, selva.mp3 */; }; @@ -1817,6 +1818,7 @@ D92F096429ED844300CC382F /* View+Marquee.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Marquee.swift"; sourceTree = ""; }; D9310C252A0BDA90006A3058 /* Jaiminho - E que quero evitar a fadiga.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "Jaiminho - E que quero evitar a fadiga.mp3"; sourceTree = ""; }; D933251D28835E41004EFACB /* ActivityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityViewController.swift; sourceTree = ""; }; + D93391F52D3307B000DC2297 /* MainSoundContainerViewModel+SpotlightIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainSoundContainerViewModel+SpotlightIndex.swift"; sourceTree = ""; }; D9344322283812AD00ADCD38 /* Author.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Author.swift; sourceTree = ""; }; D934432628381DEF00ADCD38 /* author_data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = author_data.json; sourceTree = ""; }; D93443282838230500ADCD38 /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; @@ -2776,7 +2778,7 @@ D9A9375929CDDC0900758A5A /* Michelle Bolsonaro - Senhor, meu leitinho ainda ta ralo.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "Michelle Bolsonaro - Senhor, meu leitinho ainda ta ralo.mp3"; sourceTree = ""; }; D9AB63F32A5A085800390778 /* SyncServiceStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncServiceStub.swift; sourceTree = ""; }; D9ABDDAF287411C9007B25EA /* SongHelpView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SongHelpView.swift; sourceTree = ""; }; - D9ABEEDA2CA1B50F00CA024F /* PlayRandomSoundHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayRandomSoundHelper.swift; sourceTree = ""; }; + D9ABEEDA2CA1B50F00CA024F /* ExternalActivityHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalActivityHelper.swift; sourceTree = ""; }; D9ADEB6229861A79001CB004 /* Ai eu acho exagero.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "Ai eu acho exagero.mp3"; sourceTree = ""; }; D9ADEB6429861BD2001CB004 /* Bem-vindos ao devido processo legal.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "Bem-vindos ao devido processo legal.mp3"; sourceTree = ""; }; D9ADEB6529861BD2001CB004 /* Ana Priscila Azevedo (golpista) - Senhores, selva.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "Ana Priscila Azevedo (golpista) - Senhores, selva.mp3"; sourceTree = ""; }; @@ -4784,7 +4786,7 @@ D97B2DD728E5E9E400C3B50B /* ViewAid */ = { isa = PBXGroup; children = ( - D9ABEEDA2CA1B50F00CA024F /* PlayRandomSoundHelper.swift */, + D9ABEEDA2CA1B50F00CA024F /* ExternalActivityHelper.swift */, D95DB0F72882491A00D4FEDF /* ScreenAndTabs.swift */, D97B2DD828E5EA0500C3B50B /* DeleteFolderViewAide.swift */, D9A3AB6A28F03C970064CB7A /* ViewStates.swift */, @@ -5088,6 +5090,7 @@ D94EAB5C29FC4D51004CD604 /* Sync */, D98BB6992AC68769001DE23D /* FloatingSelectionOptionsView.swift */, D96370042B085845002E8838 /* ExplicitDisabledWarning.swift */, + D93391F52D3307B000DC2297 /* MainSoundContainerViewModel+SpotlightIndex.swift */, ); path = Sounds; sourceTree = ""; @@ -6957,7 +6960,7 @@ D9CA301E2A03EE2300F1F23D /* NetworkRabbit+Generic.swift in Sources */, D9D3D4912887495B00C42FDE /* FolderList.swift in Sources */, D97B2DD928E5EA0500C3B50B /* DeleteFolderViewAide.swift in Sources */, - D9ABEEDB2CA1B51800CA024F /* PlayRandomSoundHelper.swift in Sources */, + D9ABEEDB2CA1B51800CA024F /* ExternalActivityHelper.swift in Sources */, D9592C442AA2F59B00157F5A /* AddSongAndMusicGenreTables.swift in Sources */, D904495A2BB283C20023088B /* ExternalLinkButton.swift in Sources */, D9DC68902AD38648005D3F81 /* RetroBanner.swift in Sources */, @@ -6973,6 +6976,7 @@ D98D13822CD5C4B1001CF5BB /* AddChangeHashFieldToUserFolderTable.swift in Sources */, D9943F172863DAE1002C6B97 /* FolderDetailView.swift in Sources */, D91E375A2C4C8F33003A1107 /* PushAction.swift in Sources */, + D93391F62D3307B100DC2297 /* MainSoundContainerViewModel+SpotlightIndex.swift in Sources */, D949181228860ABC0079F545 /* AllFoldersiPadView.swift in Sources */, D9B8DE7F2BE5CEFD000C9035 /* ReactionSound.swift in Sources */, D9F45DD72BED30C400D49340 /* DynamicBanner.swift in Sources */, diff --git a/MedoDelirioBrasilia/Resources/Assets.xcassets/spotlightIndexSoundImage.imageset/Contents.json b/MedoDelirioBrasilia/Resources/Assets.xcassets/spotlightIndexSoundImage.imageset/Contents.json new file mode 100644 index 000000000..b1fea519f --- /dev/null +++ b/MedoDelirioBrasilia/Resources/Assets.xcassets/spotlightIndexSoundImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Simulator Screenshot - iPad (10th generation) - 2025-01-11 at 23.57.55.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MedoDelirioBrasilia/Resources/Assets.xcassets/spotlightIndexSoundImage.imageset/Simulator Screenshot - iPad (10th generation) - 2025-01-11 at 23.57.55.png b/MedoDelirioBrasilia/Resources/Assets.xcassets/spotlightIndexSoundImage.imageset/Simulator Screenshot - iPad (10th generation) - 2025-01-11 at 23.57.55.png new file mode 100644 index 000000000..e13655712 Binary files /dev/null and b/MedoDelirioBrasilia/Resources/Assets.xcassets/spotlightIndexSoundImage.imageset/Simulator Screenshot - iPad (10th generation) - 2025-01-11 at 23.57.55.png differ diff --git a/MedoDelirioBrasilia/Sources/AppSupport/MedoDelirioBrasiliaApp.swift b/MedoDelirioBrasilia/Sources/AppSupport/MedoDelirioBrasiliaApp.swift index c6d2d79e3..2d93fbaa6 100644 --- a/MedoDelirioBrasilia/Sources/AppSupport/MedoDelirioBrasiliaApp.swift +++ b/MedoDelirioBrasilia/Sources/AppSupport/MedoDelirioBrasiliaApp.swift @@ -6,6 +6,7 @@ // import SwiftUI +import CoreSpotlight var moveDatabaseIssue: String = .empty @@ -17,12 +18,13 @@ struct MedoDelirioBrasiliaApp: App { @State private var tabSelection: PhoneTab = .sounds @State private var state: PadScreen? = PadScreen.allSounds - @StateObject private var helper = PlayRandomSoundHelper() + @StateObject private var helper = ExternalActivityHelper() var body: some Scene { WindowGroup { MainView(tabSelection: $tabSelection, padSelection: $state) .onOpenURL(perform: handleURL) + .onContinueUserActivity(CSSearchableItemActionType, perform: handleUserActivity) .environmentObject(helper) } } @@ -47,6 +49,38 @@ struct MedoDelirioBrasiliaApp: App { } } } + + private func handleUserActivity(_ userActivity: NSUserActivity) { + if let uniqueIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String { + let components = uniqueIdentifier.split(separator: "-", maxSplits: 1) + guard + components.count == 2, + let type = components.first, + let id = components.last + else { + print("Invalid identifier format") + return + } + + switch type { + case "sound": + tabSelection = .sounds + state = .allSounds + helper.soundIdToHighlight = String(id) + Analytics().send(action: "didUseSpotlightSearch(sound - \(uniqueIdentifier))") + case "song": + print("Navigate to Song") + case "folder": + print("Navigate to Folder") + case "reaction": + tabSelection = .reactions + //helper.soundIdToHighlight = String(id) + //Analytics().send(action: "didUseSpotlightSearch(\(uniqueIdentifier))") + default: + print("Unknown type") + } + } + } } class AppDelegate: NSObject, UIApplicationDelegate { diff --git a/MedoDelirioBrasilia/Sources/Models/ViewAid/PlayRandomSoundHelper.swift b/MedoDelirioBrasilia/Sources/Models/ViewAid/ExternalActivityHelper.swift similarity index 52% rename from MedoDelirioBrasilia/Sources/Models/ViewAid/PlayRandomSoundHelper.swift rename to MedoDelirioBrasilia/Sources/Models/ViewAid/ExternalActivityHelper.swift index b657d4389..eb51a51b2 100644 --- a/MedoDelirioBrasilia/Sources/Models/ViewAid/PlayRandomSoundHelper.swift +++ b/MedoDelirioBrasilia/Sources/Models/ViewAid/ExternalActivityHelper.swift @@ -1,5 +1,5 @@ // -// PlayRandomSoundHelper.swift +// ExternalActivityHelper.swift // MedoDelirioBrasilia // // Created by Rafael Schmitt on 23/09/24. @@ -7,7 +7,8 @@ import Foundation -class PlayRandomSoundHelper: ObservableObject { +class ExternalActivityHelper: ObservableObject { @Published var soundIdToPlay: String = "" + @Published var soundIdToHighlight: String = "" } diff --git a/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainer.swift b/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainer.swift index 38b4ada98..8235103be 100644 --- a/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainer.swift +++ b/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainer.swift @@ -40,7 +40,7 @@ struct MainSoundContainer: View { @EnvironmentObject var trendsHelper: TrendsHelper @EnvironmentObject var settingsHelper: SettingsHelper - @EnvironmentObject var playRandomSoundHelper: PlayRandomSoundHelper + @EnvironmentObject var activityHelper: ExternalActivityHelper // MARK: - Computed Properties @@ -247,13 +247,16 @@ struct MainSoundContainer: View { displayLongUpdateBanner = viewModel.totalUpdateCount >= 10 && viewModel.processedUpdateNumber != viewModel.totalUpdateCount } } - .onChange(of: playRandomSoundHelper.soundIdToPlay) { soundId in + .onChange(of: activityHelper.soundIdToPlay) { soundId in if !soundId.isEmpty { viewModel.currentViewMode = .allSounds allSoundsViewModel.scrollAndPlaySound(withId: soundId) - playRandomSoundHelper.soundIdToPlay = "" + activityHelper.soundIdToPlay = "" } } + .onChange(of: activityHelper.soundIdToHighlight) { soundId in + highlight(soundId: soundId) + } .sheet(isPresented: $showingModalView) { SyncInfoView( lastUpdateAttempt: AppPersistentMemory().getLastUpdateAttempt(), @@ -334,6 +337,10 @@ struct MainSoundContainer: View { Task { showRetroBanner = await ClassicRetroView.ViewModel.shouldDisplayBanner() } + + Task { + await viewModel.addReactionsToSpotlight() + } } .onChange(of: scenePhase) { newPhase in if newPhase == .active { diff --git a/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainerViewModel+SpotlightIndex.swift b/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainerViewModel+SpotlightIndex.swift new file mode 100644 index 000000000..eda1b365e --- /dev/null +++ b/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainerViewModel+SpotlightIndex.swift @@ -0,0 +1,75 @@ +// +// MainSoundContainerViewModel+SpotlightIndex.swift +// MedoDelirioBrasilia +// +// Created by Rafael Schmitt on 11/01/25. +// + +import Foundation +import CoreSpotlight +import UIKit + +extension MainSoundContainerViewModel { + + public func index(sounds: [Sound]) { + sounds.forEach { sound in + addContentToIndex(sound) + } + } + + public func addReactionsToSpotlight() async { + let serverReactions = try? await ReactionRepository().allReactions() + serverReactions?.forEach { reaction in + addReactionToIndex(reaction) + } + } + + private func addReactionToIndex(_ reaction: Reaction) { + let attributeSet = CSSearchableItemAttributeSet(contentType: UTType.audio) + attributeSet.title = "Reação \"\(reaction.title)\"" + //attributeSet.thumbnailData = reaction.image + + let item = CSSearchableItem( + uniqueIdentifier: "reaction-\(reaction.id)", + domainIdentifier: "\(Bundle.main.bundleIdentifier ?? "").reactions", + attributeSet: attributeSet + ) + + let defaultIndex = CSSearchableIndex.default() + defaultIndex.indexSearchableItems([item]) { error in + if let error { + return print(error.localizedDescription) + } + print("\(reaction.title) indexed.") + } + } + + private func addContentToIndex(_ sound: Sound) { + let attributeSet = CSSearchableItemAttributeSet(contentType: UTType.audio) + attributeSet.title = sound.title + attributeSet.artist = sound.authorName + attributeSet.duration = NSNumber(value: sound.duration) + attributeSet.thumbnailData = cover() + + let item = CSSearchableItem( + uniqueIdentifier: "sound-\(sound.id)", + domainIdentifier: "\(Bundle.main.bundleIdentifier ?? "").sounds", + attributeSet: attributeSet + ) + + let defaultIndex = CSSearchableIndex.default() + defaultIndex.indexSearchableItems([item]) { error in + if let error { + return print(error.localizedDescription) + } + print("\(sound.title) indexed.") + } + } + + private func cover() -> Data? { + guard let image = UIImage(named: "spotlightIndexSoundImage") else { + return nil + } + return image.pngData() + } +} diff --git a/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainerViewModel.swift b/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainerViewModel.swift index c8fc40180..a64797196 100644 --- a/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainerViewModel.swift +++ b/MedoDelirioBrasilia/Sources/Views/Sounds/MainSoundContainerViewModel.swift @@ -98,6 +98,8 @@ class MainSoundContainerViewModel: ObservableObject { guard sounds.count > 0 else { return } let sortOption: SoundSortOption = SoundSortOption(rawValue: soundSortOption) ?? .dateAddedDescending sortAllSounds(by: sortOption) + + index(sounds: sounds) } catch { print("Erro carregando sons: \(error.localizedDescription)") dataLoadingDidFail = true