Commit 0487db74 authored by shigemi miura's avatar shigemi miura

Push通知

parent 4780c488
//
// AlertDB.swift
// Sailassist
//
// Created by 三浦薫巳 on 2023/11/19.
//
import Foundation
struct WarnRecord {
var title: String
var body: String
let soundName: String
}
enum SwitchingEca {
case Finish
case Start
case Notice
init() {
self = .Notice
}
}
class AlertDB {
// シングルトン宣言
static let OnlyOne = AlertDB()
private var alertEca : [SwitchingEca: WarnRecord] = [
SwitchingEca.Finish: WarnRecord(title: NSLocalizedString("Eca Finish", comment: ""), body: NSLocalizedString("Arrived at switching finished point", comment: ""), soundName: "sound_eca.mp3")
, SwitchingEca.Start: WarnRecord(title: NSLocalizedString("Eca Start", comment: ""), body: NSLocalizedString("Arrivaed at switching start point", comment: ""), soundName: "sound_eca.mp3")
, SwitchingEca.Notice: WarnRecord(title: NSLocalizedString("Eca Notice", comment: ""), body: NSLocalizedString("Arrived at advance notice point", comment: ""), soundName: "sound_eca.mp3")
]
//Eca通知
func GetAlertEcaPoint(point : SwitchingEca) -> WarnRecord {
return alertEca[point]!
}
}
//
// PushNotificationTypes.swift
// Sailassist
//
// Created by 三浦薫巳 on 2023/11/19.
//
import Foundation
struct PushNotificationTypes {
//リモートPush用sendType
enum SendType: Int32{
case Error = -1
case Eca = 0 // Eca
init(){
self = .Error
}
init?(_ code : Int32) {
switch code {
case 0: self = .Eca
default: self = .Error
}
}
}
//ローカルPush用Identifier
enum LocalPushIdentifier: String{
case Reserve = "Reserve" // 予約(初期設定)
case EcaSwitching = "Eca" // Eca
}
}
......@@ -16,6 +16,21 @@ class EcaTask {
sessionShipStatus.RequestShipStatus(responseShipStatus)
}
/**
* Eca通知
*/
private func notificationEca(point: SwitchingEca) {
let alertDB = AlertDB.OnlyOne
var wernrec = WarnRecord(title:"Eca", body:"", soundName:"")
wernrec = alertDB.GetAlertEcaPoint(point: point)
let formatstr = wernrec.title
wernrec.title = String(format: formatstr)
let voicemanager = AlertManager.OnlyOne
voicemanager.VocalizeAlert(alertrec: wernrec, identifier: .EcaSwitching)
}
func responseShipStatus(result: Result<Data, APIError>) {
print(debug: "called")
switch result {
......@@ -29,6 +44,10 @@ class EcaTask {
SharingData.my.server?.latitude = res.lat
SharingData.my.server?.longitude = res.lon
SharingData.my.dataTime = res.dataTime //2023-11-02T05:25:49.4362123Z
if Preferences.LocationType == 1 {
SharingData.my.location = SharingData.my.server
}
}
checkEca()
case .failure(let errorCode):
......@@ -37,20 +56,20 @@ class EcaTask {
}
}
private func checkEca() {
func checkEca() {
let runningEca = eca.ecaArea.first(where: {(key, value) in value.isRunning == true})
if let eca = runningEca?.value {
if let location = SharingData.my.location {
let distance = LocationCalculation.checkPolyLine(objPos: eca.points, shipPos: location)
if eca.swStart >= Float(distance) {
notificationEca(point: SwitchingEca.Start)
}
if eca.swNotice >= Float(distance) {
notificationEca(point: SwitchingEca.Notice)
}
if eca.swFinish >= Float(distance) {
notificationEca(point: SwitchingEca.Finish)
}
}
}
......
......@@ -22,6 +22,9 @@ enum HttpRequestType : String {
case GetMessage = "https://ssv-canary-web.azurewebsites.net/api/chatdata/getmessages?shipId=XXXXX"
case SignalR = "https://ssv-canary-web.azurewebsites.net/signalr/shore"
case UploadImage = "https://ssv-canary-web.azurewebsites.net/api/chatdata/uploadimage"
case ConnectionString = "Endpoint=sb://ssv-canary-notification.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=rj2WniCJWWP2SG5gk4J7/2P/nIyChx8+JXKWCfTi8KA="
case HubName = "ssv-canary-notification"
}
#else
//MARK: 運用サーバー用
......
......@@ -6,7 +6,9 @@
<string>pk.eyJ1Ijoiam1hcmluZWNsb3VkIiwiYSI6ImNsbmxjbGYzZjA0dG8yaW82MDgwajQ5OTQifQ.pd8YC9qK1C4YmMUbMx6ywQ</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>location</string>
<string>remote-notification</string>
</array>
</dict>
</plist>
......@@ -43,6 +43,11 @@ class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
lastSeenLocation = locations.first
let targetCoordinate : CLLocationCoordinate2D = lastSeenLocation!.coordinate
SharingData.my.gps = targetCoordinate
if Preferences.LocationType == 0 {
SharingData.my.location = SharingData.my.gps
let ecaTask = EcaTask()
ecaTask.checkEca()
}
}
}
}
......
......@@ -26,7 +26,9 @@ class Preferences{
@AppStorage(wrappedValue:"", PreferencesKey.TypeString.ShipId.rawValue) static var ShipId: String
@AppStorage(wrappedValue:"", PreferencesKey.TypeString.ShipPassword.rawValue) static var ShipPassword: String
@AppStorage(wrappedValue:"", PreferencesKey.TypeString.UserName.rawValue) static var UserName: String
// func getPreferences(key)
@AppStorage(wrappedValue:0, PreferencesKey.TypeInt.LocationType.rawValue) static var LocationType: Int
// func getPreferences(key)
@AppStorage(PreferencesKey.TypeURL.ECDIS.rawValue) static var ECDISUrl: URL?
}
......
......@@ -19,9 +19,9 @@ class PreferencesKey{
///Int型
enum TypeInt: String{
case LocationType
case AppStartUsingDate
case LastLoginDate
}
enum TypeURL: String{
......
......@@ -6,10 +6,13 @@
//
import SwiftUI
import UserNotifications
import WindowsAzureMessaging
@main
struct SailassistApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) private var delegate
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
......@@ -18,11 +21,67 @@ struct SailassistApp: App {
}
class AppDelegate: NSObject, UIApplicationDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print(debug: "called")
//プッシュ通知の利用許可のリクエスト送信
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
if error != nil {
return
}
if granted {
UNUserNotificationCenter.current().delegate = self
}
}
application.registerForRemoteNotifications()
MSNotificationHub.start(connectionString: HttpRequestType.ConnectionString.rawValue, hubName: HttpRequestType.HubName.rawValue)
return true
}
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
print(debug: "called")
let config = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
config.delegateClass = SceneDelegate.self
return config
}
func notificationHub(_ notificationHub: MSNotificationHub!, didReceivePushNotification notification: MSNotificationHubMessage!) {
let title = notification.title ?? ""
let body = notification.body ?? ""
if (UIApplication.shared.applicationState == .background) {
print("Notification received in background: title:\"\(title)\" body:\"\(body)\"")
} else {
let alertController = UIAlertController(title: title, message: body, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel))
// self.present(alertController, animated: true)
}
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
// フォアグラウンド状態で通知を受信して表示する
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
print(debug: userInfo)
completionHandler([[.banner, .badge, .sound]])
}
// バックグラウンド状態で通知を受信して表示する
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print(debug: userInfo)
completionHandler()
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate, ObservableObject {
......
//
// SignalRService.swift
// Sailassist
//
// Created by 三浦薫巳 on 2023/11/17.
//
import Foundation
import SwiftSignalRClient
public class SignalRService {
private var connection: HubConnection
public init(url: URL) {
// リクエストURLの組み立て
// let url_string : String = HttpRequestType.SignalR.rawValue
// guard let req_url = URL(string : url_string) else {
// return
// }
connection = HubConnectionBuilder(url: url).withLogging(minLogLevel: .error).build()
connection.on(method: "MessageReceived", callback: { (user: String, message: String) in
do {
self.handleMessage(message, from: user)
} catch {
print(error)
}
})
// hubConnection?.invoke(method: "Your Method Name", arguments: [arg1, arg2]) { error in
// if let error = error {
// print("An error occurred: %@", "\(error)")
// }
// }
//
// hubConnection?.on(method: "YourMethod", callback: { (chatCount: Int, notificationsCount: Int) in
// self.notificationCount = notificationsCount
// self.chatCount = chatCount
// })
connection.start()
}
private func handleMessage(_ message: String, from user: String) {
// Do something with the message.
}
func connectionDidOpen(hubConnection: SwiftSignalRClient.HubConnection) {
print("connectionDidOpen")
// After connection established call registerUserInServer method
DispatchQueue.main.async {
// self.registerUserInServer()
}
}
func connectionDidFailToOpen(error: Error) {
print("connectionDidFailToOpen")
}
func connectionDidClose(error: Error?) {
print("connectionDidClose")
}
func connectionWillReconnect(error: Error) {
print("connectionWillReconnect")
}
func connectionDidReconnect() {
print("connectionDidReconnect")
}
}
//
// AlertManager.swift
// Sailassist
//
// Created by 三浦薫巳 on 2023/11/19.
//
import Foundation
import UIKit
import AVFoundation
// 音声を扱うクラスのうち、アラートに関するもの
class AlertManager {
// シングルトン宣言
static let OnlyOne = AlertManager()
private var player: AVAudioPlayer! // ここでインスタンスを作ると例外発生
var lastAlert: PushNotificationTypes.LocalPushIdentifier = .Reserve
var lastSpeech: String = ""
private init() {
//とりあえず音源を仮で置いておく
let soundpath = Bundle.main.bundleURL.appendingPathComponent("no_sound.mp3")
do {
player = try AVAudioPlayer(contentsOf: soundpath)
} catch {
}
}
// 無音を鳴らす
func PlayNoSound( identifier : String ) {
let content = "no_sound.mp3"
lastSpeech = identifier
let soundpath = Bundle.main.bundleURL.appendingPathComponent(content)
do {
if(player.isPlaying){
print(debug: "SOUND Playing: \(content)")
//再生中の音を止めてしてしまうので再生しない
} else {
player = try AVAudioPlayer(contentsOf: soundpath)
player.play()
print(debug: "SOUND Go play: \(content)")
}
} catch {
lastSpeech = ""
print(debug: "SOUND NoSound: \(content)")
return
}
}
// 音声だけ鳴らす
func Speaking( identifier : String, content : String ) {
lastSpeech = identifier
// 音声を再生する
let soundpath = Bundle.main.bundleURL.appendingPathComponent(content)
do {
player = try AVAudioPlayer(contentsOf: soundpath)
player.play()
print(debug: "SOUND Go play: \(content)")
} catch {
lastSpeech = ""
print(debug: "SOUND NoSound: \(content)")
return
}
}
// 音声再生中止
func CancelSpeak( identifier : String ) {
if let alertPlayer = player {
if alertPlayer.isPlaying == true {
if lastSpeech == identifier {
alertPlayer.stop()
}
}
}
lastSpeech = ""
}
// 警報を鳴らす。引数の型定義はAlertDB.swift
func VocalizeAlert( alertrec : WarnRecord ) {
VocalizeAlert( alertrec: alertrec, identifier: .EcaSwitching)
}
func VocalizeAlert( alertrec : WarnRecord, identifier : PushNotificationTypes.LocalPushIdentifier ) {
print(debug: "alertrec = \(alertrec)")
// 警報を通知する
let content = UNMutableNotificationContent()
content.title = alertrec.title
content.body = alertrec.body
/* 「通知」側の音声は、再生時間上限(10sec)の関係で無音とする
* 無音声ファイル(no_sound.mp3)は予め AppDelegateで ${App}/Library/Sounds にコピー済み
*/
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "no_sound.mp3"))
// 「通知」を発生させる
let request = UNNotificationRequest( identifier: identifier.rawValue, content: content, trigger: nil )
// let request = UNNotificationRequest( identifier: "ShipWarn", content: content, trigger: nil )
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
lastAlert = identifier
// 音声を再生する
let soundpath = Bundle.main.bundleURL.appendingPathComponent(alertrec.soundName)
do {
player = try AVAudioPlayer(contentsOf: soundpath)
player.play()
print(debug: "SOUND Go play: \(alertrec.soundName)")
} catch {
lastAlert = .Reserve
print(debug: "SOUND NoSound: \(alertrec.soundName)")
return
}
}
// 警報の再生を中断する
func CancelAlert() {
CancelAlert(identifier : .EcaSwitching )
}
// 警報の再生を中断する
func CancelAlert( identifier : PushNotificationTypes.LocalPushIdentifier ) {
if let alertPlayer = player {
if alertPlayer.isPlaying == true {
if lastAlert == identifier {
alertPlayer.stop()
}
}
}
lastAlert = .Reserve
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment