From 17e7c05576aaa63437a838a1883fae024dc46ce8 Mon Sep 17 00:00:00 2001 From: QuentinArguillere Date: Tue, 19 Nov 2024 14:48:42 +0100 Subject: [PATCH] Added Shortcuts feature to the side menu, read from the config --- Linphone.xcodeproj/project.pbxproj | 16 +++++ .../arrow-square-out.imageset/Contents.json | 21 +++++++ .../arrow-square-out.svg | 1 + .../link-break.imageset/Contents.json | 21 +++++++ .../link-break.imageset/link-break.svg | 1 + Linphone/Core/CoreContext.swift | 29 ++++++++- Linphone/UI/Main/Fragments/SideMenu.swift | 3 + .../UI/Main/Fragments/SideMenuShortcut.swift | 59 +++++++++++++++++++ Linphone/UI/Main/Models/ShortcutModel.swift | 32 ++++++++++ 9 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 Linphone/Assets.xcassets/arrow-square-out.imageset/Contents.json create mode 100644 Linphone/Assets.xcassets/arrow-square-out.imageset/arrow-square-out.svg create mode 100644 Linphone/Assets.xcassets/link-break.imageset/Contents.json create mode 100644 Linphone/Assets.xcassets/link-break.imageset/link-break.svg create mode 100644 Linphone/UI/Main/Fragments/SideMenuShortcut.swift create mode 100644 Linphone/UI/Main/Models/ShortcutModel.swift diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index b894ff1be..1d65ef034 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FC2B24D36500CEA16D /* AudioRouteUtils.swift */; }; 66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FE2B24D4AC00CEA16D /* FileUtils.swift */; }; 66C492012B24DB6900CEA16D /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C492002B24DB6900CEA16D /* Log.swift */; }; + 66CD1E602CEBA19F00898718 /* SideMenuShortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66CD1E5F2CEBA19B00898718 /* SideMenuShortcut.swift */; }; + 66D382052CEB7E0A0063E1C5 /* ShortcutModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D382042CEB7E050063E1C5 /* ShortcutModel.swift */; }; 66E50A492BD12B2300AD61CA /* MeetingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E50A482BD12B2300AD61CA /* MeetingsView.swift */; }; 66E50A4B2BD12B7800AD61CA /* MeetingsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E50A4A2BD12B7800AD61CA /* MeetingsFragment.swift */; }; 66E56BC92BA4A6D7006CE56F /* MeetingsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E56BC82BA4A6D7006CE56F /* MeetingsListViewModel.swift */; }; @@ -220,6 +222,8 @@ 66C491FC2B24D36500CEA16D /* AudioRouteUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRouteUtils.swift; sourceTree = ""; }; 66C491FE2B24D4AC00CEA16D /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; 66C492002B24DB6900CEA16D /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; + 66CD1E5F2CEBA19B00898718 /* SideMenuShortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuShortcut.swift; sourceTree = ""; }; + 66D382042CEB7E050063E1C5 /* ShortcutModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutModel.swift; sourceTree = ""; }; 66E50A482BD12B2300AD61CA /* MeetingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingsView.swift; sourceTree = ""; }; 66E50A4A2BD12B7800AD61CA /* MeetingsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingsFragment.swift; sourceTree = ""; }; 66E56BC82BA4A6D7006CE56F /* MeetingsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingsListViewModel.swift; sourceTree = ""; }; @@ -427,6 +431,14 @@ path = Extensions; sourceTree = ""; }; + 66D382032CEB7DB80063E1C5 /* Models */ = { + isa = PBXGroup; + children = ( + 66D382042CEB7E050063E1C5 /* ShortcutModel.swift */, + ); + path = Models; + sourceTree = ""; + }; 66E56BC52BA45E49006CE56F /* Meetings */ = { isa = PBXGroup; children = ( @@ -589,6 +601,7 @@ D74C9CFD2ACAEC150021626A /* Fragments */, D7A03FBE2ACC2E010081A588 /* History */, 66E56BC52BA45E49006CE56F /* Meetings */, + 66D382032CEB7DB80063E1C5 /* Models */, D7A2EDD42AC180FE005D90FC /* Viewmodel */, D719ABB82ABC67BF00B41C10 /* ContentView.swift */, ); @@ -688,6 +701,7 @@ C62817332C1C7C7400DBA646 /* HelpView.swift */, C6DC4E3E2C19C289009096FD /* SideMenuEntry.swift */, C62817272C1B389700DBA646 /* SideMenuAccountRow.swift */, + 66CD1E5F2CEBA19B00898718 /* SideMenuShortcut.swift */, D7E6D04C2AEBD77600A57AAF /* CustomBottomSheet.swift */, D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */, ); @@ -1105,6 +1119,7 @@ D71FCA832AE14D6E00D2E43E /* ContactFragment.swift in Sources */, D7C3650C2AF0084000FE6142 /* EditContactViewModel.swift in Sources */, D78E062A2BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift in Sources */, + 66CD1E602CEBA19F00898718 /* SideMenuShortcut.swift in Sources */, D7B5678E2B28888F00DE63EB /* CallView.swift in Sources */, D71A0E192B485ADF0002C6CD /* ViewExtension.swift in Sources */, D759CB642C3FBD4200AC35E8 /* StartConversationFragment.swift in Sources */, @@ -1220,6 +1235,7 @@ D714DE602C1B3B34006C1F1D /* RegisterViewModel.swift in Sources */, D70C82A72C85F5910087F43F /* ConversationForwardMessageViewModel.swift in Sources */, D74DA0122C047F0700A8561D /* HistoryModel.swift in Sources */, + 66D382052CEB7E0A0063E1C5 /* ShortcutModel.swift in Sources */, D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */, C6DC4E3F2C19C289009096FD /* SideMenuEntry.swift in Sources */, D714DE622C1C4636006C1F1D /* RegisterCodeConfirmationFragment.swift in Sources */, diff --git a/Linphone/Assets.xcassets/arrow-square-out.imageset/Contents.json b/Linphone/Assets.xcassets/arrow-square-out.imageset/Contents.json new file mode 100644 index 000000000..53713f940 --- /dev/null +++ b/Linphone/Assets.xcassets/arrow-square-out.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "arrow-square-out.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/arrow-square-out.imageset/arrow-square-out.svg b/Linphone/Assets.xcassets/arrow-square-out.imageset/arrow-square-out.svg new file mode 100644 index 000000000..471fbeb3d --- /dev/null +++ b/Linphone/Assets.xcassets/arrow-square-out.imageset/arrow-square-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/link-break.imageset/Contents.json b/Linphone/Assets.xcassets/link-break.imageset/Contents.json new file mode 100644 index 000000000..f334f8ebd --- /dev/null +++ b/Linphone/Assets.xcassets/link-break.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "link-break.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/link-break.imageset/link-break.svg b/Linphone/Assets.xcassets/link-break.imageset/link-break.svg new file mode 100644 index 000000000..a1f00248e --- /dev/null +++ b/Linphone/Assets.xcassets/link-break.imageset/link-break.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift index 44ddbae7e..7b804b3af 100644 --- a/Linphone/Core/CoreContext.swift +++ b/Linphone/Core/CoreContext.swift @@ -42,7 +42,7 @@ final class CoreContext: ObservableObject { @Published var coreIsStarted: Bool = false @Published var accounts: [AccountModel] = [] @Published var enteredForeground = false - + @Published var shortcuts: [ShortcutModel] = [] private var mCore: Core! private var mIterateSuscription: AnyCancellable? @@ -148,6 +148,33 @@ final class CoreContext: ObservableObject { self.mCore.config!.setBool(section: "sip", key: "auto_answer_replacing_calls", value: false) self.mCore.config!.setBool(section: "sip", key: "deliver_imdn", value: false) + let shortcutsCount = self.mCore.config!.getInt(section: "ui", key: "shortcut_count", defaultValue: 0) + if shortcutsCount > 0 { + var shortcuts: [ShortcutModel] = [] + for i in 0...shortcutsCount { + let shortcutSection = "shortcut_\(i)" + let link = self.mCore.config!.getString(section: shortcutSection, key: "link", defaultString: "") + let linkUrl = URL(string: link) + let name = self.mCore.config!.getString(section: shortcutSection, key: "name", defaultString: "") + let iconLink = self.mCore.config!.getString(section: shortcutSection, key: "icon", defaultString: "") + let iconLinkUrl = URL(string: iconLink) + + if linkUrl == nil { + Log.error("Could not add shortcut #\(i) pointing to \(name) because the link URL '\(link)' is invalid") + continue + } + if iconLinkUrl == nil { + Log.error("Could not add shortcut #\(i) pointing to \(name) because the icon link URL '\(iconLink)' is invalid") + continue + } + shortcuts.append(ShortcutModel(linkUrl: linkUrl!, name: name, iconLinkUrl: iconLinkUrl!)) + } + + DispatchQueue.main.async { + self.shortcuts = shortcuts + } + } + self.mCoreDelegate = CoreDelegateStub(onGlobalStateChanged: { (core: Core, state: GlobalState, _: String) in if state == GlobalState.On { #if DEBUG diff --git a/Linphone/UI/Main/Fragments/SideMenu.swift b/Linphone/UI/Main/Fragments/SideMenu.swift index 2b388ca57..8db24dfb6 100644 --- a/Linphone/UI/Main/Fragments/SideMenu.swift +++ b/Linphone/UI/Main/Fragments/SideMenu.swift @@ -118,6 +118,9 @@ struct SideMenu: View { .frame(height: 1) VStack(spacing: 19) { + ForEach(0... + */ + +import SwiftUI + +struct SideMenuShortcut: View { + @ObservedObject var shortcutModel: ShortcutModel + var body: some View { + Link(destination: shortcutModel.linkUrl) { + HStack { + AsyncImage(url: shortcutModel.iconLinkUrl) { phase in + if let image = phase.image { + image + .resizable() + .aspectRatio(contentMode: .fit) + } else if phase.error != nil { + Image("link-break") + .renderingMode(.template) + .resizable() + } else { + ProgressView() + } + } + .frame(width: 20, height: 20) + Text(NSLocalizedString(shortcutModel.name, comment: shortcutModel.name)) + .default_text_style_600(styleSize: 13) + .padding(.leading, 4) + Spacer() + Image("arrow-square-out") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 20, height: 20) + } + .background() + } + } +} + +#Preview { + SideMenuShortcut(shortcutModel: ShortcutModel(linkUrl: URL("link")!, name: "name", iconLinkUrl: URL("icon")!) + ) +} diff --git a/Linphone/UI/Main/Models/ShortcutModel.swift b/Linphone/UI/Main/Models/ShortcutModel.swift new file mode 100644 index 000000000..4a281bf9c --- /dev/null +++ b/Linphone/UI/Main/Models/ShortcutModel.swift @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-iphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import Foundation + +class ShortcutModel: ObservableObject { + @Published var linkUrl: URL + @Published var name: String + @Published var iconLinkUrl: URL + + init(linkUrl: URL, name: String, iconLinkUrl: URL) { + self.linkUrl = linkUrl + self.name = name + self.iconLinkUrl = iconLinkUrl + } +}