From dbc47030cacfb6921a4b86820c04db4a3df7cdfb Mon Sep 17 00:00:00 2001 From: Ryan Lepinski Date: Mon, 7 Oct 2024 16:44:09 -0700 Subject: [PATCH] Release 2.3.0 (#20) * Update plugin to proxy 10.0.0 * Wire up lu/la * Changelog * Update changelog --- CHANGELOG.md | 12 + Package.swift | 10 +- UaCapacitorAirship.podspec | 14 +- android/build.gradle | 2 +- .../capacitor/AirshipCapacitorVersion.kt | 2 +- .../com/airship/capacitor/AirshipPlugin.kt | 30 ++ .../airship/capacitor/CapacitorAutopilot.kt | 4 +- example/android/app/build.gradle | 7 + example/android/app/capacitor.build.gradle | 1 + .../android/app/src/main/AndroidManifest.xml | 6 + .../com/example/plugin/AirshipExtender.kt | 69 ++++ .../drawable-anydpi-v24/ic_notification.xml | 15 + .../res/drawable-hdpi/ic_notification.png | Bin 0 -> 400 bytes .../res/drawable-mdpi/ic_notification.png | Bin 0 -> 263 bytes .../res/drawable-xhdpi/ic_notification.png | Bin 0 -> 508 bytes .../res/drawable-xxhdpi/ic_notification.png | Bin 0 -> 769 bytes example/android/build.gradle | 11 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/ios/App/App.xcodeproj/project.pbxproj | 385 +++++++++++++++++- .../ios/App/App/AirshipPluginExtender.swift | 26 ++ example/ios/App/App/Info.plist | 4 + .../Info.plist | 13 + .../NotificationService.swift | 5 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Assets.xcassets/Contents.json | 6 + .../WidgetBackground.colorset/Contents.json | 11 + .../ExampleAttributes.swift | 13 + .../ExampleLiveActivity.swift | 63 +++ .../ExampleWidgetExtensionBundle.swift | 9 + .../ios/App/ExampleWidgetExtension/Info.plist | 11 + example/ios/App/Podfile | 4 + example/package-lock.json | 2 +- ios/Bootloader/AirshipCapacitorBootstrap.m | 25 -- .../Public/AirshipCapacitorBootstrap.h | 8 - ios/Plugin.xcodeproj/project.pbxproj | 28 +- ios/Plugin/AirshipCapacitorAutopilot.swift | 5 +- ios/Plugin/AirshipCapacitorVersion.swift | 2 +- ios/Plugin/AirshipPlugin.swift | 50 ++- ios/Plugin/AirshipPluginLoader.swift | 11 + ios/Podfile | 5 +- package-lock.json | 4 +- package.json | 2 +- src/AirshipLiveActivityManager.ts | 76 ++++ src/AirshipLiveUpdateManager.ts | 69 ++++ src/AirshipRoot.ts | 38 +- src/EventType.ts | 3 + src/index.ts | 2 + src/types.ts | 268 ++++++++++++ 49 files changed, 1279 insertions(+), 100 deletions(-) create mode 100644 example/android/app/src/main/java/com/example/plugin/AirshipExtender.kt create mode 100644 example/android/app/src/main/res/drawable-anydpi-v24/ic_notification.xml create mode 100644 example/android/app/src/main/res/drawable-hdpi/ic_notification.png create mode 100644 example/android/app/src/main/res/drawable-mdpi/ic_notification.png create mode 100644 example/android/app/src/main/res/drawable-xhdpi/ic_notification.png create mode 100644 example/android/app/src/main/res/drawable-xxhdpi/ic_notification.png create mode 100644 example/ios/App/App/AirshipPluginExtender.swift create mode 100644 example/ios/App/ExampleNotificationServiceExtension/Info.plist create mode 100644 example/ios/App/ExampleNotificationServiceExtension/NotificationService.swift create mode 100644 example/ios/App/ExampleWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 example/ios/App/ExampleWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 example/ios/App/ExampleWidgetExtension/Assets.xcassets/Contents.json create mode 100644 example/ios/App/ExampleWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 example/ios/App/ExampleWidgetExtension/ExampleAttributes.swift create mode 100644 example/ios/App/ExampleWidgetExtension/ExampleLiveActivity.swift create mode 100644 example/ios/App/ExampleWidgetExtension/ExampleWidgetExtensionBundle.swift create mode 100644 example/ios/App/ExampleWidgetExtension/Info.plist delete mode 100644 ios/Bootloader/AirshipCapacitorBootstrap.m delete mode 100644 ios/Bootloader/Public/AirshipCapacitorBootstrap.h create mode 100644 ios/Plugin/AirshipPluginLoader.swift create mode 100644 src/AirshipLiveActivityManager.ts create mode 100644 src/AirshipLiveUpdateManager.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e6d6c2..05c8226 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Capacitor Plugin Changelog +## Version 2.3.0 - October 7, 2024 + +Minor release that updates to latest SDK versions and adds support for iOS Live Activities & Android Live Updates. + +### Changes +- Updated Airship Android SDK to [18.3.2](https://github.com/urbanairship/android-library/releases/tag/18.3.2) +- Updated Airship iOS SDK to [18.10.0](https://github.com/urbanairship/ios-library/releases/tag/18.10.0) +- Added new APIs to manage [iOS Live Activities](https://docs.airship.com/platform/mobile/ios-live-activities/) +- Added new APIs to manage [Android Live Updates](https://docs.airship.com/platform/mobile/android-live-updates/) +- Added a new [Plugin Extenders](http://localhost:1313/platform/mobile/setup/sdk/capacitor/#extending-airship) to modify the native Airship SDK after takeOff + + ## Version 2.2.0 September 25, 2024 Minor release that updates the iOS SDK to 18.9.2 and adds the method `Airship.messageCenter.showMessageCenter(messageId?: string)` that can be used to show the OOTB Message Center UI even if auto launching Message Center is disabled. This new functionality is useful if the application needs to route the user in the app before processing the display event while still being able to use the OOTB UI. diff --git a/Package.swift b/Package.swift index bdc943d..09c58f1 100644 --- a/Package.swift +++ b/Package.swift @@ -11,23 +11,17 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", branch: "main"), - .package(url: "https://github.com/urbanairship/airship-mobile-framework-proxy.git", from: "8.1.0") + .package(url: "https://github.com/urbanairship/airship-mobile-framework-proxy.git", from: "10.0.0") ], targets: [ .target( - name: "UaCapacitorAirshipPlugin", + name: "UaCapacitorAirship", dependencies: [ .product(name: "Capacitor", package: "capacitor-swift-pm"), .product(name: "Cordova", package: "capacitor-swift-pm"), .product(name: "AirshipFrameworkProxy", package: "airship-mobile-framework-proxy") ], path: "ios/Plugin" - ), - .target( - name: "UaCapacitorAirship", - dependencies: [.target(name: "UaCapacitorAirshipPlugin")], - path: "ios/Bootloader", - publicHeadersPath: "Public" ) ] ) \ No newline at end of file diff --git a/UaCapacitorAirship.podspec b/UaCapacitorAirship.podspec index f4b4833..0a1770c 100644 --- a/UaCapacitorAirship.podspec +++ b/UaCapacitorAirship.podspec @@ -13,16 +13,6 @@ Pod::Spec.new do |s| s.ios.deployment_target = '14.0' s.dependency 'Capacitor' s.swift_version = '5.1' - s.dependency "AirshipFrameworkProxy", "8.1.0" - s.default_subspecs = ["Bootloader", "Plugin"] - - - s.subspec "Bootloader" do |bootloader| - bootloader.source_files = 'ios/Bootloader/**/*.{swift,h,m,c,cc,mm,cpp}' - end - - - s.subspec "Plugin" do |plugin| - plugin.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' - end + s.dependency "AirshipFrameworkProxy", "10.0.0" + s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' end diff --git a/android/build.gradle b/android/build.gradle index 92802ff..d81c569 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ ext { - airshipProxyVersion = project.hasProperty('airshipProxyVersion') ? rootProject.ext.airshipProxyVersion : '8.1.0' + airshipProxyVersion = project.hasProperty('airshipProxyVersion') ? rootProject.ext.airshipProxyVersion : '10.0.0' } diff --git a/android/src/main/java/com/airship/capacitor/AirshipCapacitorVersion.kt b/android/src/main/java/com/airship/capacitor/AirshipCapacitorVersion.kt index 74cb9c1..5960c7c 100644 --- a/android/src/main/java/com/airship/capacitor/AirshipCapacitorVersion.kt +++ b/android/src/main/java/com/airship/capacitor/AirshipCapacitorVersion.kt @@ -3,5 +3,5 @@ package com.airship.capacitor object AirshipCapacitorVersion { - var version = "2.2.0" + var version = "2.3.0" } \ No newline at end of file diff --git a/android/src/main/java/com/airship/capacitor/AirshipPlugin.kt b/android/src/main/java/com/airship/capacitor/AirshipPlugin.kt index 4d1b727..6fcdb70 100644 --- a/android/src/main/java/com/airship/capacitor/AirshipPlugin.kt +++ b/android/src/main/java/com/airship/capacitor/AirshipPlugin.kt @@ -15,6 +15,7 @@ import com.urbanairship.android.framework.proxy.events.EventEmitter import com.urbanairship.android.framework.proxy.proxies.AirshipProxy import com.urbanairship.android.framework.proxy.proxies.EnableUserNotificationsArgs import com.urbanairship.android.framework.proxy.proxies.FeatureFlagProxy +import com.urbanairship.android.framework.proxy.proxies.LiveUpdateRequest import com.urbanairship.json.JsonList import com.urbanairship.json.JsonMap import com.urbanairship.json.JsonSerializable @@ -246,6 +247,35 @@ class AirshipPlugin : Plugin() { } } + // Live Update + "liveUpdateManager#list" -> call.resolveSuspending(method) { + val request = LiveUpdateRequest.List.fromJson(arg) + proxy.liveUpdateManager.list(request) + } + + "liveUpdateManager#listAll" -> call.resolveSuspending(method) { + proxy.liveUpdateManager.listAll() + } + + "liveUpdateManager#start" -> call.resolveSuspending(method) { + val request = LiveUpdateRequest.Start.fromJson(arg) + proxy.liveUpdateManager.start(request) + } + + "liveUpdateManager#update" -> call.resolveSuspending(method) { + val request = LiveUpdateRequest.Update.fromJson(arg) + proxy.liveUpdateManager.update(request) + } + + "liveUpdateManager#end" -> call.resolveSuspending(method) { + val request = LiveUpdateRequest.End.fromJson(arg) + proxy.liveUpdateManager.end(request) + } + + "liveUpdateManager#clearAll" -> call.resolveSuspending(method) { + proxy.liveUpdateManager.clearAll() + } + else -> call.reject("Not implemented") } } diff --git a/android/src/main/java/com/airship/capacitor/CapacitorAutopilot.kt b/android/src/main/java/com/airship/capacitor/CapacitorAutopilot.kt index 540f325..b857009 100644 --- a/android/src/main/java/com/airship/capacitor/CapacitorAutopilot.kt +++ b/android/src/main/java/com/airship/capacitor/CapacitorAutopilot.kt @@ -16,9 +16,7 @@ import com.urbanairship.json.JsonValue class CapacitorAutopilot : BaseAutopilot() { - override fun onAirshipReady(airship: UAirship) { - super.onAirshipReady(airship) - + override fun onReady(context: Context, airship: UAirship) { Log.i("CapacitorAutopilot", "onAirshipReady") airship.analytics.registerSDKExtension(Extension.CAPACITOR, AirshipCapacitorVersion.version) } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 900d9d5..6aceb84 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,6 +1,13 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' android { + + kotlinOptions { + jvmTarget = '17' + } + namespace "com.example.plugin" compileSdk rootProject.ext.compileSdkVersion defaultConfig { diff --git a/example/android/app/capacitor.build.gradle b/example/android/app/capacitor.build.gradle index e6cda9b..d33fbe6 100644 --- a/example/android/app/capacitor.build.gradle +++ b/example/android/app/capacitor.build.gradle @@ -12,6 +12,7 @@ dependencies { implementation project(':capacitor-camera') implementation project(':capacitor-splash-screen') implementation project(':ua-capacitor-airship') + implementation 'androidx.core:core-ktx:1.13.1' } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 4d7ca38..aee7933 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -9,6 +9,12 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> + + + + { + + if (event == LiveUpdateEvent.END) { + // Dismiss the live update on END. The default behavior will leave the Live Update + // in the notification tray until the dismissal time is reached or the user dismisses it. + return LiveUpdateResult.cancel() + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val importance = NotificationManager.IMPORTANCE_DEFAULT + val channel = NotificationChannel("emoji-example", "Emoji example", importance) + channel.description = "Emoji example" + NotificationManagerCompat.from(context).createNotificationChannel(channel) + } + + val launchIntent = context.packageManager + .getLaunchIntentForPackage(context.packageName) + ?.addCategory(update.name) + ?.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP) + ?.setPackage(null) + + val contentIntent = PendingIntent.getActivity( + context, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE + ) + + val notification = NotificationCompat.Builder(context, "emoji-example") + .setSmallIcon(R.drawable.ic_notification) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setCategory(NotificationCompat.CATEGORY_EVENT) + .setContentTitle("Example Live Update") + .setContentText(update.content.requireField("emoji")) + .setContentIntent(contentIntent) + + return LiveUpdateResult.ok(notification) + } +} \ No newline at end of file diff --git a/example/android/app/src/main/res/drawable-anydpi-v24/ic_notification.xml b/example/android/app/src/main/res/drawable-anydpi-v24/ic_notification.xml new file mode 100644 index 0000000..6f92d40 --- /dev/null +++ b/example/android/app/src/main/res/drawable-anydpi-v24/ic_notification.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/example/android/app/src/main/res/drawable-hdpi/ic_notification.png b/example/android/app/src/main/res/drawable-hdpi/ic_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..8e46a11c9d8a440db92b01c5e6c3405dcddd31ed GIT binary patch literal 400 zcmV;B0dM|^P)Y5QY;YsjS3G0$NxJLP%$738b_3FW71!wu1ITYRlM|CO<%IwGbqTKR`RbKoqO< zU7a(NmrLB;9M?Dx3>@>$JiEJZhF+?D%wy%uUl<`!l5 zZfKkm$mT`rcvRni@vOcV*c%OGH6@N^iM>%kw8!Qt*kykZ5S+0w2l{-D7g#)DQ;u}` zyoq3;&Ia&-df}!Auc&u!f+nAv2?l3*6I`Rpk{@w6fn(Vzqi#3^)zQ;4z)**`z?RBG z{ujP)Rsym3&3c$X#`eyon?3i;{N-@Q@-8C1$p- zRmYsHlc`QNQg?ln4oVUl+xOIKdh0aQ@7(oKS`^;@$_M$9;r+<(cmg_Vp^0~3&Z8a_ z4tTLajTmi$Yldrs>xk>ZmV92f+IY-d%9vc;65F}28-y}1r~6xOz_|^IytOz4RDQey@J*WGAkb9wFa8Y5bl=> z51pZlJ?o=km0dmE60_)OwD|gXGA0O1$1cL!>wTDo{C|JV~TKk+|s^r%J>*?%M`V ylnvas4IC&NxNi$&UMP1ZuYTNHQ4~c{{w+WD0K?;5U@pA?00001xa`HsqZ8+$xP4CkaNyC=bUrSIp>^n&N=6t z``?8S)~lYYKC0I6d{r!q>XT|-HOlkRX!z&w&r6=Kie*XUTHyI;H2iuZh}p)T*py*A zGR?9l8Vw&;{YixR&7RmSLn4o58)r{c3UEFV$Q)-+EH)ci&atNw3i2}%%6@}u*;6)~ z*`D3;gFTf|n8`#i^N>Advzf>+$(~9n(02r!U=M}XzvHc{eX3)snReONcFfLYzI;&6 zruNK^MfOxqp*A4oE1VxyO{mU>^iv@(yXH)2t&w_dqwn9OTfMGAC~DQjSw zIV+(cvrJYa*O)gd1$e+jHFTf3qX?~gErVj7`AemN&zN8UJmo%0p+Q$Ud4OEuehQ@l z)0{Yfrns*{XyFmnFHY;>z3Mg(mdIw=%sgxlBi!Grg{AM-yLla!zL7l5rUIS9Ec+0_ zGGzIdyl&OB;9gGXqUY_+P|nMNInFUUzA>b41NX9r*REK|_vq<)4@T#Xvbg|9IHyDB z7TMfIF^{5<|IO!eKcK^GF2Etq=?M9LKxT&5_Dzd?<*Gf{!@BE@DjmPa<^tT}9HZl7 zA>UKXeBt$d(E?kzVunWnw@CH<6q^c^`j;JAw{V}ZDZ}RE8hz2l@3>k{cT^X7uq5{P z@q+3uPvrEL`_7FkH5K=ri>}+vg&J%Z_n%1@UFIqcc8UAXq>C1~N`rmi{xflfrsMuI zae=1e{+AW%@%dJ4_4*{lIp>^n&N=6tb1U>0sIm73YTcz>00000NkvXXu0mjfpfG /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -210,11 +404,44 @@ buildActionMask = 2147483647; files = ( 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, + 6EDAFB042CB0A1C3000BD4AA /* ExampleAttributes.swift in Sources */, + 6EDAFB062CB0A205000BD4AA /* AirshipPluginExtender.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDAFAC72CB09FED000BD4AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDAFADB2CB0A09A000BD4AA /* NotificationService.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDAFADD2CB0A0E2000BD4AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDAFB032CB0A142000BD4AA /* ExampleAttributes.swift in Sources */, + 6EDAFB002CB0A0EA000BD4AA /* ExampleWidgetExtensionBundle.swift in Sources */, + 6EDAFB012CB0A0EA000BD4AA /* ExampleLiveActivity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 6EDAFAD12CB09FED000BD4AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6EDAFACA2CB09FED000BD4AA /* ExampleNotificationServiceExtension */; + targetProxy = 6EDAFAD02CB09FED000BD4AA /* PBXContainerItemProxy */; + }; + 6EDAFAF12CB0A0E3000BD4AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6EDAFAE02CB0A0E2000BD4AA /* ExampleWidgetExtensionExtension */; + targetProxy = 6EDAFAF02CB0A0E3000BD4AA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 504EC30B1FED79650016851F /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -385,6 +612,144 @@ }; name = Release; }; + 6EDAFAD42CB09FED000BD4AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 61B0B698FBF55FF5913F0A2D /* Pods-ExampleNotificationServiceExtension.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = PGJV57GD94; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ExampleNotificationServiceExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ExampleNotificationServiceExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.urbanairship.richpush.ExampleNotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6EDAFAD52CB09FED000BD4AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CFA38E0210D8797479732AAC /* Pods-ExampleNotificationServiceExtension.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = PGJV57GD94; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ExampleNotificationServiceExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ExampleNotificationServiceExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.urbanairship.richpush.ExampleNotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 6EDAFAF32CB0A0E3000BD4AA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = PGJV57GD94; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ExampleWidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ExampleWidgetExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.urbanairship.richpush.ExampleWidgetExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6EDAFAF42CB0A0E3000BD4AA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = PGJV57GD94; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ExampleWidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ExampleWidgetExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.urbanairship.richpush.ExampleWidgetExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -406,6 +771,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6EDAFAD72CB09FED000BD4AA /* Build configuration list for PBXNativeTarget "ExampleNotificationServiceExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6EDAFAD42CB09FED000BD4AA /* Debug */, + 6EDAFAD52CB09FED000BD4AA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6EDAFAF62CB0A0E3000BD4AA /* Build configuration list for PBXNativeTarget "ExampleWidgetExtensionExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6EDAFAF32CB0A0E3000BD4AA /* Debug */, + 6EDAFAF42CB0A0E3000BD4AA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 504EC2FC1FED79650016851F /* Project object */; diff --git a/example/ios/App/App/AirshipPluginExtender.swift b/example/ios/App/App/AirshipPluginExtender.swift new file mode 100644 index 0000000..17019e4 --- /dev/null +++ b/example/ios/App/App/AirshipPluginExtender.swift @@ -0,0 +1,26 @@ +import Foundation +import AirshipKit +import AirshipFrameworkProxy +import ActivityKit + +@objc(AirshipPluginExtender) +public class AirshipPluginExtender: NSObject, AirshipPluginExtenderProtocol { + + /// Called on the same run loop when Airship takesOff. + @MainActor + public static func onAirshipReady() { + + if #available(iOS 16.1, *) { + // Throws if setup is called more than once + try? LiveActivityManager.shared.setup { configurator in + + // Register each activity type + await configurator.register(forType: Activity.self) { attributes in + // Track this property as the Airship name for updates + attributes.name + } + } + } + } + +} diff --git a/example/ios/App/App/Info.plist b/example/ios/App/App/Info.plist index b9a2be6..e07a8c1 100644 --- a/example/ios/App/App/Info.plist +++ b/example/ios/App/App/Info.plist @@ -45,5 +45,9 @@ UIViewControllerBasedStatusBarAppearance + NSSupportsLiveActivities + + NSSupportsLiveActivitiesFrequentUpdates + diff --git a/example/ios/App/ExampleNotificationServiceExtension/Info.plist b/example/ios/App/ExampleNotificationServiceExtension/Info.plist new file mode 100644 index 0000000..57421eb --- /dev/null +++ b/example/ios/App/ExampleNotificationServiceExtension/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/example/ios/App/ExampleNotificationServiceExtension/NotificationService.swift b/example/ios/App/ExampleNotificationServiceExtension/NotificationService.swift new file mode 100644 index 0000000..f130896 --- /dev/null +++ b/example/ios/App/ExampleNotificationServiceExtension/NotificationService.swift @@ -0,0 +1,5 @@ +import AirshipServiceExtension +import UserNotifications + +class NotificationService: UANotificationServiceExtension { +} diff --git a/example/ios/App/ExampleWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example/ios/App/ExampleWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example/ios/App/ExampleWidgetExtension/Assets.xcassets/Contents.json b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example/ios/App/ExampleWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example/ios/App/ExampleWidgetExtension/ExampleAttributes.swift b/example/ios/App/ExampleWidgetExtension/ExampleAttributes.swift new file mode 100644 index 0000000..e9fade9 --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/ExampleAttributes.swift @@ -0,0 +1,13 @@ +import ActivityKit +import WidgetKit +import SwiftUI + +struct ExampleAttributes: ActivityAttributes { + public struct ContentState: Codable, Hashable { + // Dynamic stateful properties about your activity go here! + var emoji: String + } + + // Fixed non-changing properties about your activity go here! + var name: String +} diff --git a/example/ios/App/ExampleWidgetExtension/ExampleLiveActivity.swift b/example/ios/App/ExampleWidgetExtension/ExampleLiveActivity.swift new file mode 100644 index 0000000..2806c45 --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/ExampleLiveActivity.swift @@ -0,0 +1,63 @@ +import ActivityKit +import WidgetKit +import SwiftUI + +struct ExampleLiveActivity: Widget { + var body: some WidgetConfiguration { + ActivityConfiguration(for: ExampleAttributes.self) { context in + // Lock screen/banner UI goes here + VStack { + Text("Hello \(context.state.emoji)") + } + .activityBackgroundTint(Color.cyan) + .activitySystemActionForegroundColor(Color.black) + + } dynamicIsland: { context in + DynamicIsland { + // Expanded UI goes here. Compose the expanded UI through + // various regions, like leading/trailing/center/bottom + DynamicIslandExpandedRegion(.leading) { + Text("Leading") + } + DynamicIslandExpandedRegion(.trailing) { + Text("Trailing") + } + DynamicIslandExpandedRegion(.bottom) { + Text("Bottom \(context.state.emoji)") + // more content + } + } compactLeading: { + Text("L") + } compactTrailing: { + Text("T \(context.state.emoji)") + } minimal: { + Text(context.state.emoji) + } + .widgetURL(URL(string: "http://www.apple.com")) + .keylineTint(Color.red) + } + } +} + +extension ExampleAttributes { + fileprivate static var preview: ExampleAttributes { + ExampleAttributes(name: "World") + } +} + +extension ExampleAttributes.ContentState { + fileprivate static var smiley: ExampleAttributes.ContentState { + ExampleAttributes.ContentState(emoji: "😀") + } + + fileprivate static var starEyes: ExampleAttributes.ContentState { + ExampleAttributes.ContentState(emoji: "🤩") + } +} + +#Preview("Notification", as: .content, using: ExampleAttributes.preview) { + ExampleLiveActivity() +} contentStates: { + ExampleAttributes.ContentState.smiley + ExampleAttributes.ContentState.starEyes +} diff --git a/example/ios/App/ExampleWidgetExtension/ExampleWidgetExtensionBundle.swift b/example/ios/App/ExampleWidgetExtension/ExampleWidgetExtensionBundle.swift new file mode 100644 index 0000000..8293bea --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/ExampleWidgetExtensionBundle.swift @@ -0,0 +1,9 @@ +import WidgetKit +import SwiftUI + +@main +struct ExampleWidgetExtensionBundle: WidgetBundle { + var body: some Widget { + ExampleLiveActivity() + } +} diff --git a/example/ios/App/ExampleWidgetExtension/Info.plist b/example/ios/App/ExampleWidgetExtension/Info.plist new file mode 100644 index 0000000..0f118fb --- /dev/null +++ b/example/ios/App/ExampleWidgetExtension/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/example/ios/App/Podfile b/example/ios/App/Podfile index f92baa1..cf3be53 100644 --- a/example/ios/App/Podfile +++ b/example/ios/App/Podfile @@ -21,6 +21,10 @@ target 'App' do # Add your Pods here end +target 'ExampleNotificationServiceExtension' do + pod 'AirshipServiceExtension' +end + post_install do |installer| assertDeploymentTarget(installer) end diff --git a/example/package-lock.json b/example/package-lock.json index 5d5d92a..d407ab2 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -23,7 +23,7 @@ }, "..": { "name": "@ua/capacitor-airship", - "version": "2.1.0", + "version": "2.3.0", "license": "Apache-2.0", "devDependencies": { "@capacitor/android": "^6.0.0", diff --git a/ios/Bootloader/AirshipCapacitorBootstrap.m b/ios/Bootloader/AirshipCapacitorBootstrap.m deleted file mode 100644 index faeeb1d..0000000 --- a/ios/Bootloader/AirshipCapacitorBootstrap.m +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright Airship and Contributors */ - -#import "AirshipCapacitorBootstrap.h" - -#if __has_include() -#import -#elif __has_include("UACapacitorAirship-Swift.h") -#import "UaCapacitorAirship-Swift.h" -#else -@import UaCapacitorAirshipPlugin; -#endif - -@implementation AirshipCapacitorBootstrap - - -+ (void)load { - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserverForName:UIApplicationDidFinishLaunchingNotification - object:nil - queue:nil usingBlock:^(NSNotification * _Nonnull note) { - - [AirshipCapacitorAutopilot.shared onApplicationDidFinishLaunchingWithLaunchOptions:note.userInfo]; - }]; -} -@end diff --git a/ios/Bootloader/Public/AirshipCapacitorBootstrap.h b/ios/Bootloader/Public/AirshipCapacitorBootstrap.h deleted file mode 100644 index c342fe1..0000000 --- a/ios/Bootloader/Public/AirshipCapacitorBootstrap.h +++ /dev/null @@ -1,8 +0,0 @@ -/* Copyright Airship and Contributors */ - -@import Foundation; -@import UIKit; - -@interface AirshipCapacitorBootstrap: NSObject - -@end diff --git a/ios/Plugin.xcodeproj/project.pbxproj b/ios/Plugin.xcodeproj/project.pbxproj index 11ba317..bfbc4ef 100644 --- a/ios/Plugin.xcodeproj/project.pbxproj +++ b/ios/Plugin.xcodeproj/project.pbxproj @@ -13,9 +13,8 @@ 50ADFF97201F53D600D50D53 /* AirshipTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* AirshipTests.swift */; }; 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; 50E1A94820377CB70090CE1A /* AirshipPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* AirshipPlugin.swift */; }; + 6EDAFAC02CB096AF000BD4AA /* AirshipPluginLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDAFABF2CB09698000BD4AA /* AirshipPluginLoader.swift */; }; 6EDE5F202B9FCF3600E33D04 /* AirshipCapacitorVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDE5F1C2B9FCF3600E33D04 /* AirshipCapacitorVersion.swift */; }; - 6EDE5F212B9FCF3600E33D04 /* AirshipCapacitorBootstrap.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EDE5F1D2B9FCF3600E33D04 /* AirshipCapacitorBootstrap.h */; }; - 6EDE5F222B9FCF3600E33D04 /* AirshipCapacitorBootstrap.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EDE5F1E2B9FCF3600E33D04 /* AirshipCapacitorBootstrap.m */; }; 6EDE5F232B9FCF3600E33D04 /* AirshipCapacitorAutopilot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDE5F1F2B9FCF3600E33D04 /* AirshipCapacitorAutopilot.swift */; }; /* End PBXBuildFile section */ @@ -39,9 +38,8 @@ 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50E1A94720377CB70090CE1A /* AirshipPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AirshipPlugin.swift; sourceTree = ""; }; 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; + 6EDAFABF2CB09698000BD4AA /* AirshipPluginLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirshipPluginLoader.swift; sourceTree = ""; }; 6EDE5F1C2B9FCF3600E33D04 /* AirshipCapacitorVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AirshipCapacitorVersion.swift; sourceTree = ""; }; - 6EDE5F1D2B9FCF3600E33D04 /* AirshipCapacitorBootstrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AirshipCapacitorBootstrap.h; sourceTree = ""; }; - 6EDE5F1E2B9FCF3600E33D04 /* AirshipCapacitorBootstrap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AirshipCapacitorBootstrap.m; sourceTree = ""; }; 6EDE5F1F2B9FCF3600E33D04 /* AirshipCapacitorAutopilot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AirshipCapacitorAutopilot.swift; sourceTree = ""; }; 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; @@ -74,7 +72,6 @@ 50ADFF7E201F53D600D50D53 = { isa = PBXGroup; children = ( - 6ECEF8102C34B0B300E7144C /* Bootloader */, 50ADFF8A201F53D600D50D53 /* Plugin */, 50ADFF95201F53D600D50D53 /* PluginTests */, 50ADFF89201F53D600D50D53 /* Products */, @@ -95,6 +92,7 @@ 50ADFF8A201F53D600D50D53 /* Plugin */ = { isa = PBXGroup; children = ( + 6EDAFABF2CB09698000BD4AA /* AirshipPluginLoader.swift */, 6EDE5F1F2B9FCF3600E33D04 /* AirshipCapacitorAutopilot.swift */, 6EDE5F1C2B9FCF3600E33D04 /* AirshipCapacitorVersion.swift */, 50E1A94720377CB70090CE1A /* AirshipPlugin.swift */, @@ -112,23 +110,6 @@ path = PluginTests; sourceTree = ""; }; - 6ECEF8102C34B0B300E7144C /* Bootloader */ = { - isa = PBXGroup; - children = ( - 6ECEF8112C34B12A00E7144C /* Public */, - 6EDE5F1E2B9FCF3600E33D04 /* AirshipCapacitorBootstrap.m */, - ); - path = Bootloader; - sourceTree = ""; - }; - 6ECEF8112C34B12A00E7144C /* Public */ = { - isa = PBXGroup; - children = ( - 6EDE5F1D2B9FCF3600E33D04 /* AirshipCapacitorBootstrap.h */, - ); - path = Public; - sourceTree = ""; - }; 8C8E7744173064A9F6D438E3 /* Pods */ = { isa = PBXGroup; children = ( @@ -157,7 +138,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 6EDE5F212B9FCF3600E33D04 /* AirshipCapacitorBootstrap.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -329,9 +309,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6EDE5F222B9FCF3600E33D04 /* AirshipCapacitorBootstrap.m in Sources */, 50E1A94820377CB70090CE1A /* AirshipPlugin.swift in Sources */, 6EDE5F202B9FCF3600E33D04 /* AirshipCapacitorVersion.swift in Sources */, + 6EDAFAC02CB096AF000BD4AA /* AirshipPluginLoader.swift in Sources */, 6EDE5F232B9FCF3600E33D04 /* AirshipCapacitorAutopilot.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/Plugin/AirshipCapacitorAutopilot.swift b/ios/Plugin/AirshipCapacitorAutopilot.swift index 078dde8..8592d32 100644 --- a/ios/Plugin/AirshipCapacitorAutopilot.swift +++ b/ios/Plugin/AirshipCapacitorAutopilot.swift @@ -9,10 +9,8 @@ import AirshipCore import AirshipFrameworkProxy import Capacitor -@objc public final class AirshipCapacitorAutopilot: NSObject { - @objc public static let shared: AirshipCapacitorAutopilot = AirshipCapacitorAutopilot() @MainActor @@ -36,8 +34,7 @@ public final class AirshipCapacitorAutopilot: NSObject { */ @MainActor - @objc - public func onApplicationDidFinishLaunching( + func onApplicationDidFinishLaunching( launchOptions: [UIApplication.LaunchOptionsKey : Any]? ) { AirshipProxy.shared.delegate = self diff --git a/ios/Plugin/AirshipCapacitorVersion.swift b/ios/Plugin/AirshipCapacitorVersion.swift index fdcd022..6b18be7 100644 --- a/ios/Plugin/AirshipCapacitorVersion.swift +++ b/ios/Plugin/AirshipCapacitorVersion.swift @@ -3,5 +3,5 @@ import Foundation class AirshipCapacitorVersion { - static let version = "2.2.0" + static let version = "2.3.0" } diff --git a/ios/Plugin/AirshipPlugin.swift b/ios/Plugin/AirshipPlugin.swift index 88bd4ba..65c05f0 100644 --- a/ios/Plugin/AirshipPlugin.swift +++ b/ios/Plugin/AirshipPlugin.swift @@ -32,7 +32,8 @@ public class AirshipPlugin: CAPPlugin, CAPBridgedPlugin { .displayPreferenceCenter: "display_preference_center", .notificationResponseReceived: "notification_response_received", .pushReceived: "push_received", - .notificationStatusChanged: "notification_status_changed" + .notificationStatusChanged: "notification_status_changed", + .liveActivitiesUpdated: "ios_live_activities_updated" ] @MainActor @@ -476,6 +477,53 @@ public class AirshipPlugin: CAPPlugin, CAPBridgedPlugin { ) return nil + + // Live Activity Manager + case "liveActivityManager#list": + if #available(iOS 16.1, *) { + return try await LiveActivityManager.shared.list( + try call.requireCodableArg() + ) + } else { + throw AirshipErrors.error("Live Activities only available on 16.1+") + } + + case "liveActivityManager#listAll": + if #available(iOS 16.1, *) { + return try await LiveActivityManager.shared.listAll() + } else { + throw AirshipErrors.error("Live Activities only available on 16.1+") + } + + case "liveActivityManager#start": + if #available(iOS 16.1, *) { + return try await LiveActivityManager.shared.start( + try call.requireCodableArg() + ) + } else { + throw AirshipErrors.error("Live Activities only available on 16.1+") + } + + case "liveActivityManager#update": + if #available(iOS 16.1, *) { + try await LiveActivityManager.shared.update( + try call.requireCodableArg() + ) + } else { + throw AirshipErrors.error("Live Activities only available on 16.1+") + } + return nil + + case "liveActivityManager#end": + if #available(iOS 16.1, *) { + try await LiveActivityManager.shared.end( + try call.requireCodableArg() + ) + } else { + throw AirshipErrors.error("Live Activities only available on 16.1+") + } + return nil + default: throw AirshipErrors.error("Not implemented \(method)") } diff --git a/ios/Plugin/AirshipPluginLoader.swift b/ios/Plugin/AirshipPluginLoader.swift new file mode 100644 index 0000000..4991116 --- /dev/null +++ b/ios/Plugin/AirshipPluginLoader.swift @@ -0,0 +1,11 @@ +import AirshipFrameworkProxy + +@objc(AirshipPluginLoader) +public class AirshipPluginLoader: NSObject, AirshipPluginLoaderProtocol { + @MainActor + public static func onApplicationDidFinishLaunching( + launchOptions: [UIApplication.LaunchOptionsKey : Any]? + ) { + AirshipCapacitorAutopilot.shared.onApplicationDidFinishLaunching(launchOptions: launchOptions) + } +} diff --git a/ios/Podfile b/ios/Podfile index 9d768ad..53ff1ac 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -5,7 +5,7 @@ def capacitor_pods use_frameworks! pod 'Capacitor', :path => '../node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../node_modules/@capacitor/ios' - pod 'AirshipFrameworkProxy', '8.1.0' + pod 'AirshipFrameworkProxy', '10.0.0' end target 'Plugin' do @@ -24,4 +24,5 @@ post_install do |installer| end end end -end \ No newline at end of file +end + diff --git a/package-lock.json b/package-lock.json index 4d0121e..c58d891 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ua/capacitor-airship", - "version": "2.1.0", + "version": "2.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ua/capacitor-airship", - "version": "2.1.0", + "version": "2.3.0", "license": "Apache-2.0", "devDependencies": { "@capacitor/android": "^6.0.0", diff --git a/package.json b/package.json index bb8dcbe..9851e8d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ua/capacitor-airship", - "version": "2.2.0", + "version": "2.3.0", "description": "Airship capacitor plugin", "main": "dist/plugin.cjs.js", "module": "dist/esm/index.js", diff --git a/src/AirshipLiveActivityManager.ts b/src/AirshipLiveActivityManager.ts new file mode 100644 index 0000000..7397d9c --- /dev/null +++ b/src/AirshipLiveActivityManager.ts @@ -0,0 +1,76 @@ +import { + LiveActivity, + LiveActivityListRequest, + LiveActivityStartRequest, + LiveActivityUpdateRequest, + LiveActivityEndRequest, + LiveActivitiesUpdatedEvent, +} from './types'; + +import type { AirshipPluginWrapper } from './AirshipPlugin'; +import { EventType } from './EventType'; +import { PluginListenerHandle } from '@capacitor/core'; + +/** + * Live Activity manager. + */ +export class AirshipLiveActivityManager { + constructor(private readonly plugin: AirshipPluginWrapper) {} + + /** + * Lists any Live Activities for the request. + * @param request The request options. + * @returns A promise with the result. + */ + public list(request: LiveActivityListRequest): Promise { + return this.plugin.perform('liveActivityManager#list', request); + } + + /** + * Lists all Live Activities. + * @param request The request options. + * @returns A promise with the result. + */ + public listAll(): Promise { + return this.plugin.perform('liveActivityManager#listAll'); + } + + /** + * Starts a Live Activity. + * @param request The request options. + * @returns A promise with the result. + */ + public start(request: LiveActivityStartRequest): Promise { + return this.plugin.perform('liveActivityManager#start', request); + } + + /** + * Updates a Live Activity. + * @param request The request options. + * @returns A promise with the result. + */ + public update(request: LiveActivityUpdateRequest): Promise { + return this.plugin.perform('liveActivityManager#update', request); + } + + /** + * Ends a Live Activity. + * @param request The request options. + * @returns A promise with the result. + */ + public end(request: LiveActivityEndRequest): Promise { + return this.plugin.perform('liveActivityManager#end', request); + } + + /** + * Adds a Live Activity listener. + */ + public onLiveActivityUpdates( + listener: (event: LiveActivitiesUpdatedEvent) => void, + ): Promise { + return this.plugin.addListener( + EventType.IOSLiveActivitiesUpdated, + listener, + ); + } +} diff --git a/src/AirshipLiveUpdateManager.ts b/src/AirshipLiveUpdateManager.ts new file mode 100644 index 0000000..690e778 --- /dev/null +++ b/src/AirshipLiveUpdateManager.ts @@ -0,0 +1,69 @@ +import { + LiveUpdate, + LiveUpdateListRequest, + LiveUpdateStartRequest, + LiveUpdateUpdateRequest, + LiveUpdateEndRequest, +} from './types'; + +import type { AirshipPluginWrapper } from './AirshipPlugin'; + +/** + * Live Update manager. + */ +export class AirshipLiveUpdateManager { + constructor(private readonly plugin: AirshipPluginWrapper) {} + + /** + * Lists any Live Updates for the request. + * @param request The request options. + * @returns A promise with the result. + */ + public list(request: LiveUpdateListRequest): Promise { + return this.plugin.perform('liveUpdateManager#list', request); + } + + /** + * Lists all Live Updates. + * @returns A promise with the result. + */ + public listAll(): Promise { + return this.plugin.perform('liveUpdateManager#listAll'); + } + + /** + * Starts a Live Update. + * @param request The request options. + * @returns A promise with the result. + */ + public start(request: LiveUpdateStartRequest): Promise { + return this.plugin.perform('liveUpdateManager#start', request); + } + + /** + * Updates a Live Update. + * @param request The request options. + * @returns A promise with the result. + */ + public update(request: LiveUpdateUpdateRequest): Promise { + return this.plugin.perform('liveUpdateManager#update', request); + } + + /** + * Ends a Live Update. + * @param request The request options. + * @returns A promise with the result. + */ + public end(request: LiveUpdateEndRequest): Promise { + return this.plugin.perform('liveUpdateManager#end', request); + } + + /** + * Clears all Live Updates. + * @returns A promise with the result. + */ + public clearAll(): Promise { + return this.plugin.perform('liveUpdateManager#clearAll'); + } +} + diff --git a/src/AirshipRoot.ts b/src/AirshipRoot.ts index 44c79dc..fbdf8d3 100644 --- a/src/AirshipRoot.ts +++ b/src/AirshipRoot.ts @@ -13,7 +13,9 @@ import { AirshipPrivacyManager } from './AirshipPrivacyManager'; import { AirshipPush } from './AirshipPush'; import { EventType } from './EventType'; import type { AirshipPluginWrapper } from './AirshipPlugin'; -import type { AirshipConfig , DeepLinkEvent } from './types'; +import type { AirshipConfig, DeepLinkEvent } from './types'; +import { AirshipLiveActivityManager } from './AirshipLiveActivityManager'; +import { AirshipLiveUpdateManager } from './AirshipLiveUpdateManager'; /** * Airship @@ -31,6 +33,16 @@ export class AirshipRoot { public readonly push: AirshipPush; public readonly featureFlagManager: AirshipFeatureFlagManager; + /** + * iOS only accessors + */ + public readonly iOS: AirshipRootIOS; + + /** + * iOS only accessors + */ + public readonly android: AirshipRootAndroid; + constructor(private readonly plugin: AirshipPluginWrapper) { this.actions = new AirshipActions(plugin); this.analytics = new AirshipAnalytics(plugin); @@ -43,6 +55,8 @@ export class AirshipRoot { this.privacyManager = new AirshipPrivacyManager(plugin); this.push = new AirshipPush(plugin); this.featureFlagManager = new AirshipFeatureFlagManager(plugin); + this.iOS = new AirshipRootIOS(plugin); + this.android = new AirshipRootAndroid(plugin); } /** @@ -67,7 +81,25 @@ export class AirshipRoot { /** * Adds a deep link listener. */ - public onDeepLink(listener: (event: DeepLinkEvent) => void): Promise { - return this.plugin.addListener(EventType.DeepLink, listener) + public onDeepLink( + listener: (event: DeepLinkEvent) => void, + ): Promise { + return this.plugin.addListener(EventType.DeepLink, listener); + } +} + +export class AirshipRootIOS { + public readonly liveActivityManager: AirshipLiveActivityManager; + + constructor(plugin: AirshipPluginWrapper) { + this.liveActivityManager = new AirshipLiveActivityManager(plugin); + } +} + +export class AirshipRootAndroid { + public readonly liveUpdateManager: AirshipLiveUpdateManager; + + constructor(plugin: AirshipPluginWrapper) { + this.liveUpdateManager = new AirshipLiveUpdateManager(plugin); } } diff --git a/src/EventType.ts b/src/EventType.ts index b7a3a3b..87ce48f 100644 --- a/src/EventType.ts +++ b/src/EventType.ts @@ -9,6 +9,7 @@ import type { DisplayMessageCenterEvent, DisplayPreferenceCenterEvent, PushTokenReceivedEvent, + LiveActivitiesUpdatedEvent, } from './types'; export enum EventType { @@ -22,6 +23,7 @@ export enum EventType { DisplayPreferenceCenter = 'display_preference_center', PushTokenReceived = 'push_token_received', IOSAuthorizedNotificationSettingsChanged = 'ios_authorized_notification_settings_changed', + IOSLiveActivitiesUpdated = 'ios_live_activities_updated', } export interface EventTypeMap { @@ -35,4 +37,5 @@ export interface EventTypeMap { [EventType.DisplayMessageCenter]: DisplayMessageCenterEvent; [EventType.DisplayPreferenceCenter]: DisplayPreferenceCenterEvent; [EventType.PushTokenReceived]: PushTokenReceivedEvent; + [EventType.IOSLiveActivitiesUpdated]: LiveActivitiesUpdatedEvent; } diff --git a/src/index.ts b/src/index.ts index 76e37fb..d43ef6e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,8 @@ export { AirshipMessageCenter } from './AirshipMessageCenter'; export { AirshipPreferenceCenter } from './AirshipPreferenceCenter'; export { AirshipPrivacyManager } from './AirshipPrivacyManager'; export { AirshipFeatureFlagManager } from './AirshipFeatureFlagManager'; +export { AirshipLiveActivityManager } from './AirshipLiveActivityManager'; +export { AirshipLiveUpdateManager } from './AirshipLiveUpdateManager'; export { AirshipPush, AirshipPushAndroid, AirshipPushIOS } from './AirshipPush'; export { SubscriptionListEditor } from './SubscriptionListEditor'; diff --git a/src/types.ts b/src/types.ts index 5379cec..eebb4bf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -215,6 +215,17 @@ export interface DisplayPreferenceCenterEvent { preferenceCenterId: string; } +/** + * Event fired whenever any of the Live Activities update, create, or end. + */ +export interface LiveActivitiesUpdatedEvent { + /** + * The Live Activities. + */ + activities: LiveActivity[]; +} + + /** * Custom event */ @@ -644,6 +655,10 @@ export interface InboxMessage { * The message sent date in milliseconds. */ sentDate: number; + /** + * Optional - The message expiration date in milliseconds. + */ + expirationDate?: number; /** * Optional - The icon url for the message. */ @@ -824,3 +839,256 @@ export interface FeatureFlag { */ readonly _internal: unknown; } + + +/** + * Live Activity info. + */ +export interface LiveActivity { + /** + * The activity ID. + */ + id: string; + /** + * The attribute types. + */ + attributeTypes: string; + /** + * The content. + */ + content: LiveActivityContent; + /** + * The attributes. + */ + attributes: JsonObject; +} + +/** + * Live Activity content. + */ +export interface LiveActivityContent { + /** + * The content state. + */ + state: JsonObject; + /** + * Optional ISO 8601 date string that defines when the Live Activity will be stale. + */ + staleDate?: string; + /** + * The relevance score. + */ + relevanceScore: number; +} + +/** + * Base Live Activity request. + */ +export interface LiveActivityRequest { + /** + * Attributes types. This should match the Activity type of your Live Activity. + */ + attributesType: string; +} + +/** + * Live Activity list request. + */ +export interface LiveActivityListRequest extends LiveActivityRequest {} + +/** + * Live Activity start request. + */ +export interface LiveActivityStartRequest extends LiveActivityRequest { + /** + * Dynamic content. + */ + content: LiveActivityContent; + /** + * Fixed attributes. + */ + attributes: JsonObject; +} + +/** + * Live Activity update request. + */ +export interface LiveActivityUpdateRequest extends LiveActivityRequest { + /** + * The Live Activity ID to update. + */ + activityId: string; + /** + * Dynamic content. + */ + content: LiveActivityContent; +} + +/** + * Live Activity end request. + */ +export interface LiveActivityEndRequest extends LiveActivityRequest { + /** + * The Live Activity ID to update. + */ + activityId: string; + /** + * Dynamic content. + */ + content?: LiveActivityContent; + + /** + * Dismissal policy. Defaults to `LiveActivityDismissalPolicyDefault`. + */ + dismissalPolicy?: LiveActivityDismissalPolicy; +} + +export type LiveActivityDismissalPolicy = + | LiveActivityDismissalPolicyImmediate + | LiveActivityDismissalPolicyDefault + | LiveActivityDismissalPolicyAfterDate; + +/** + * Dismissal policy to immediately dismiss the Live Activity on end. + */ +export interface LiveActivityDismissalPolicyImmediate { + type: 'immediate'; +} + +/** + * Dismissal policy to dismiss the Live Activity after the expiration. + */ +export interface LiveActivityDismissalPolicyDefault { + type: 'default'; +} + +/** + * Dismissal policy to dismiss the Live Activity after a given date. + */ +export interface LiveActivityDismissalPolicyAfterDate { + type: 'after'; + // ISO 8601 date string. + date: string; +} + +/** + * Live Update info. + */ +export interface LiveUpdate { + /** + * The Live Update name. + */ + name: string; + + /** + * The Live Update type. + */ + type: string; + + /** + * Dynamic content. + */ + content: JsonObject; + + /** + * ISO 8601 date string of the last content update. + */ + lastContentUpdateTimestamp: string; + + /** + * ISO 8601 date string of the last state update. + */ + lastStateChangeTimestamp: string; + + /** + * Optional ISO 8601 date string that defines when to end this Live Update. + */ + dismissTimestamp?: string; +} + + +/** + * Live Update list request. + */ +export interface LiveUpdateListRequest { + type: string; +} + +/** + * Live Update update request. + */ +export interface LiveUpdateUpdateRequest { + /** + * The Live Update name. + */ + name: string; + /** + * Dynamic content. + */ + content: JsonObject; + + /** + * Optional ISO 8601 date string, used to filter out of order updates/ + */ + timestamp?: string; + + /** + * Optional ISO 8601 date string that defines when to end this Live Update. + */ + dismissTimestamp?: string; +} + +/** + * Live Update end request. + */ +export interface LiveUpdateEndRequest { + /** + * The Live Update name. + */ + name: string; + + /** + * Dynamic content. + */ + content?: JsonObject; + + /** + * Optional ISO 8601 date string, used to filter out of order updates/ + */ + timestamp?: string; + + /** + * Optional ISO 8601 date string that defines when to end this Live Update. + */ + dismissTimestamp?: string; +} + +/** + * Live Update start request. + */ +export interface LiveUpdateStartRequest { + /** + * The Live Update name. + */ + name: string; + + /** + * The Live Update type. + */ + type: string; + + /** + * Dynamic content. + */ + content: JsonObject; + + /** + * Optional ISO 8601 date string, used to filter out of order updates/ + */ + timestamp?: string; + + /** + * Optional ISO 8601 date string that defines when to end this Live Update. + */ + dismissTimestamp?: string; +} \ No newline at end of file