diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3693dd64..5176e5e4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,8 +3,8 @@ name: CI
on: [pull_request]
env:
- DEVELOPER_DIR: /Applications/Xcode_15.3.app/Contents/Developer
- FLUTTER_VERSION: 3.22.0
+ DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
+ FLUTTER_VERSION: 3.24.3
JAVA_VERSION: "17.x"
JAVA_DISTRIBUTION: 'zulu'
ANDROID_SDK_ROOT: ${{ github.workspace }}/android-sdk
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4c784d70..2b0d8be2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -6,8 +6,8 @@ on:
- "[0-9]+.[0-9]+.[0-9]+*"
env:
- DEVELOPER_DIR: /Applications/Xcode_15.3.app/Contents/Developer
- FLUTTER_VERSION: 3.22.0
+ DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
+ FLUTTER_VERSION: 3.24.3
JAVA_VERSION: "17.x"
JAVA_DISTRIBUTION: 'zulu'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 62340563..3f931fd7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,29 @@
# Flutter Plugin Changelog
+## Version 7.9.0 - October 20, 2024
+
+Minor version release with several new features including: iOS Live Activities, Android Live Updates, Message Center improvements, and iOS notification service extension support in the iOS example project.
+
+### Changes
+
+- Updated Airship Android SDK to [18.3.3](https://github.com/urbanairship/android-library/releases/tag/18.3.3)
+- Updated Airship iOS SDK to [18.11.1](https://github.com/urbanairship/ios-library/releases/tag/18.11.1)
+- Added `notificationPermissionStatus` to `PushNotificationStatus`
+- Added options to `enableUserNotifications` to specify the `PromptPermissionFallback` when enabling user notifications
+- Added new `showMessageCenter(messageId?: string)` and `showMessageView(messageId: string)` to `MessageCenter` to display the OOTB UI even if `autoLaunchDefaultMessageCenter` is disabled
+- 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 [iOS plugin extender]() to modify the native Airship SDK after takeOff
+- Added new [Android plugin extender]() to modify the native Airship SDK after takeOff
+
+## Version 7.8.2 - September 13, 2024
+
+Patch release that fixes a potential Swift compile error.
+
+### Changes
+
+- Fixed a potential Swift compile error.
+
## Version 7.8.2 - September 13, 2024
Patch release that fixes a potential Swift compile error.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 12f7f5c3..9a8aeb35 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -2,4 +2,5 @@ include: package:lints/recommended.yaml
linter:
rules:
- constant_identifier_names: false
\ No newline at end of file
+ constant_identifier_names: false
+ prefer_const_constructors: false
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
index 20b79e13..49c6831e 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
group 'com.airship.airship'
version '1.0-SNAPSHOT'
@@ -7,7 +5,7 @@ buildscript {
ext.kotlin_version = '1.9.0'
ext.coroutine_version = '1.5.2'
ext.datastore_preferences_version = '1.1.1'
- ext.airship_framework_proxy_version = '7.3.0'
+ ext.airship_framework_proxy_version = '10.1.1'
repositories {
@@ -45,19 +43,17 @@ android {
lintOptions {
disable 'InvalidPackage'
}
+
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
- namespace = 'com.airship.airship'
-}
-
-tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
- freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
- jvmTarget = "17"
+ jvmTarget = '17'
}
+
+ namespace = 'com.airship.airship'
}
dependencies {
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 510dabb4..1c84c693 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -3,5 +3,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/src/main/kotlin/com/airship/flutter/AirshipPlugin.kt b/android/src/main/kotlin/com/airship/flutter/AirshipPlugin.kt
index 7e84ce19..affaa021 100644
--- a/android/src/main/kotlin/com/airship/flutter/AirshipPlugin.kt
+++ b/android/src/main/kotlin/com/airship/flutter/AirshipPlugin.kt
@@ -3,13 +3,16 @@ package com.airship.flutter
import android.app.Activity
import android.content.Context
import android.os.Build
+import android.util.Log
import com.urbanairship.actions.ActionResult
-import com.urbanairship.android.framework.proxy.Event
import com.urbanairship.android.framework.proxy.EventType
import com.urbanairship.android.framework.proxy.events.EventEmitter
import com.urbanairship.android.framework.proxy.proxies.AirshipProxy
import com.urbanairship.android.framework.proxy.proxies.FeatureFlagProxy
+import com.urbanairship.android.framework.proxy.proxies.LiveUpdateRequest
+import com.urbanairship.android.framework.proxy.proxies.EnableUserNotificationsArgs
import com.urbanairship.json.JsonValue
+import com.urbanairship.json.toJsonList
import io.flutter.embedding.engine.FlutterShellArgs
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
@@ -25,6 +28,8 @@ import io.flutter.plugin.platform.PlatformViewRegistry
import kotlinx.coroutines.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
+import kotlinx.coroutines.Dispatchers
+
class AirshipPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
@@ -109,6 +114,7 @@ class AirshipPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
override fun onMethodCall(call: MethodCall, result: Result) {
val proxy = AirshipProxy.shared(context)
+ val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
when (call.method) {
// Flutter
@@ -116,6 +122,7 @@ class AirshipPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
// Airship
"takeOff" -> result.resolveResult(call) { proxy.takeOff(call.jsonArgs()) }
+
"isFlying" -> result.resolveResult(call) { proxy.isFlying() }
// Channel
@@ -153,9 +160,32 @@ class AirshipPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
// Push
"push#setUserNotificationsEnabled" -> result.resolveResult(call) { proxy.push.setUserNotificationsEnabled(call.booleanArg()) }
- "push#enableUserNotifications" -> result.resolvePending(call) { proxy.push.enableUserPushNotifications() }
+ "push#enableUserNotifications" -> {
+ val args = call.jsonArgs()
+
+ val enableArgs = args?.let {
+ try {
+ EnableUserNotificationsArgs.fromJson(it)
+ } catch (e: Exception) {
+ null
+ }
+ }
+
+ coroutineScope.launch {
+ try {
+ val enableResult = proxy.push.enableUserPushNotifications(enableArgs)
+ result.success(enableResult)
+ } catch (e: Exception) {
+ result.error("ERROR", "Failed to enable user notifications", e.localizedMessage)
+ }
+ }
+ }
"push#isUserNotificationsEnabled" -> result.resolveResult(call) { proxy.push.isUserNotificationsEnabled() }
- "push#getNotificationStatus" -> result.resolveResult(call) { proxy.push.getNotificationStatus() }
+ "push#getNotificationStatus" -> result.resolveResult(call) {
+ coroutineScope.launch {
+ proxy.push.getNotificationStatus()
+ }
+ }
"push#getActiveNotifications" -> result.resolveResult(call) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
proxy.push.getActiveNotifications()
@@ -197,6 +227,8 @@ class AirshipPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
"messageCenter#getMessages" -> result.resolveResult(call) {
JsonValue.wrapOpt(proxy.messageCenter.getMessages())
}
+ "messageCenter#showMessageCenter" -> result.resolveResult(call) { proxy.messageCenter.showMessageCenter(call.optStringArg()) }
+ "messageCenter#showMessageView" -> result.resolveResult(call) { proxy.messageCenter.showMessageView(call.stringArg()) }
"messageCenter#display" -> result.resolveResult(call) { proxy.messageCenter.display(call.optStringArg()) }
"messageCenter#markMessageRead" -> result.resolveResult(call) { proxy.messageCenter.markMessageRead(call.stringArg()) }
"messageCenter#deleteMessage" -> result.resolveResult(call) { proxy.messageCenter.deleteMessage(call.stringArg()) }
@@ -248,6 +280,121 @@ class AirshipPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
}
}
+ // Live Activities
+
+ "featureFlagManager#trackInteraction" -> {
+ result.resolveDeferred(call) { callback ->
+ scope.launch {
+ try {
+ val args = call.jsonArgs()
+
+ val wrapped = JsonValue.wrap(args)
+ val featureFlagProxy = FeatureFlagProxy(wrapped)
+ proxy.featureFlagManager.trackInteraction(flag = featureFlagProxy)
+ callback(null, null)
+ } catch (e: Exception) {
+ callback(null, e)
+ }
+ }
+ }
+ }
+
+ "liveUpdate#start" -> result.resolveResult(call) {
+ try {
+ val args = call.jsonArgs()
+ Log.d("AirshipPlugin", "Received args for liveUpdate#start: $args")
+
+ val startRequest = LiveUpdateRequest.Start.fromJson(args)
+
+ Log.d("AirshipPlugin", "Created LiveUpdateRequest.Start: $startRequest")
+
+ proxy.liveUpdateManager.start(startRequest)
+ Log.d("AirshipPlugin", "LiveUpdate start method called successfully")
+
+ null // Return null as the start function doesn't return anything
+ } catch (e: Exception) {
+ throw e
+ }
+ }
+
+ "liveUpdate#update" -> result.resolveResult(call) {
+ try {
+ val args = call.jsonArgs()
+ Log.d("AirshipPlugin", "Received args for liveUpdate#update: $args")
+
+ val updateRequest = LiveUpdateRequest.Update.fromJson(args)
+
+ proxy.liveUpdateManager.update(updateRequest)
+ Log.d("AirshipPlugin", "LiveUpdate update method called successfully")
+ null
+ } catch (e: Exception) {
+ Log.e("AirshipPlugin", "Error processing liveUpdate#update request", e)
+ throw e
+ }
+ }
+
+ "liveUpdate#list" -> result.resolveDeferred(call) { callback ->
+ try {
+ val args = call.jsonArgs()
+ Log.d("AirshipPlugin", "Received args for liveUpdate#list: $args")
+
+ val listRequest = LiveUpdateRequest.List.fromJson(args)
+
+ coroutineScope.launch {
+ try {
+ val liveUpdates = proxy.liveUpdateManager.list(listRequest)
+ Log.d("AirshipPlugin", "LiveUpdate list method completed successfully")
+ callback(liveUpdates.toJsonList(), null)
+ } catch (e: Exception) {
+ Log.e("AirshipPlugin", "Error listing LiveUpdates", e)
+ callback(null, e)
+ }
+ }
+ } catch (e: Exception) {
+ Log.e("AirshipPlugin", "Error processing liveUpdate#list request", e)
+ callback(null, e)
+ }
+ }
+
+ "liveUpdate#listAll" -> result.resolveDeferred(call) { callback ->
+ coroutineScope.launch {
+ try {
+ val liveUpdates = proxy.liveUpdateManager.listAll()
+ Log.d("AirshipPlugin", "LiveUpdate listAll method completed successfully")
+ callback(JsonValue.wrap(liveUpdates), null)
+ } catch (e: Exception) {
+ Log.e("AirshipPlugin", "Error listing all LiveUpdates", e)
+ callback(null, e)
+ }
+ }
+ }
+
+ "liveUpdate#end" -> result.resolveResult(call) {
+ try {
+ val args = call.jsonArgs()
+ Log.d("AirshipPlugin", "Received args for liveUpdate#end: $args")
+
+ val endRequest = LiveUpdateRequest.End.fromJson(args)
+
+ proxy.liveUpdateManager.end(endRequest)
+ Log.d("AirshipPlugin", "LiveUpdate end method called successfully")
+ null
+ } catch (e: Exception) {
+ Log.e("AirshipPlugin", "Error processing liveUpdate#end request", e)
+ throw e
+ }
+ }
+
+ "liveUpdate#clearAll" -> result.resolveResult(call) {
+ try {
+ proxy.liveUpdateManager.clearAll()
+ Log.d("AirshipPlugin", "LiveUpdate clearAll method called successfully")
+ null
+ } catch (e: Exception) {
+ Log.e("AirshipPlugin", "Error processing liveUpdate#clearAll request", e)
+ throw e
+ }
+ }
// Feature Flag
"featureFlagManager#flag" -> result.resolveDeferred(call) { callback ->
diff --git a/android/src/main/kotlin/com/airship/flutter/AirshipPluginExtender.kt b/android/src/main/kotlin/com/airship/flutter/AirshipPluginExtender.kt
new file mode 100644
index 00000000..0379fc48
--- /dev/null
+++ b/android/src/main/kotlin/com/airship/flutter/AirshipPluginExtender.kt
@@ -0,0 +1,16 @@
+/* Copyright Urban Airship and Contributors */
+
+package com.urbanairship.reactnative
+
+import android.content.Context
+import com.urbanairship.UAirship
+
+/**
+ * Extender that will be called during takeOff to customize the airship instance.
+ * Register the extender fully qualified class name in the manifest under the key
+ * `com.urbanairship.flutter.AIRSHIP_EXTENDER`.
+ */
+@Deprecated("Use com.urbanairship.android.framework.proxy.AirshipPluginExtender instead and register it under the manifest key `com.urbanairship.plugin.extender`")
+interface AirshipExtender {
+ fun onAirshipReady(context: Context, airship: UAirship)
+}
\ No newline at end of file
diff --git a/android/src/main/kotlin/com/airship/flutter/AirshipPluginVersion.kt b/android/src/main/kotlin/com/airship/flutter/AirshipPluginVersion.kt
index 4c3b2459..b6898be8 100644
--- a/android/src/main/kotlin/com/airship/flutter/AirshipPluginVersion.kt
+++ b/android/src/main/kotlin/com/airship/flutter/AirshipPluginVersion.kt
@@ -2,6 +2,6 @@ package com.airship.flutter
class AirshipPluginVersion {
companion object {
- const val AIRSHIP_PLUGIN_VERSION = "7.8.2"
+ const val AIRSHIP_PLUGIN_VERSION = "7.9.0"
}
}
diff --git a/android/src/main/kotlin/com/airship/flutter/FlutterAutopilot.kt b/android/src/main/kotlin/com/airship/flutter/FlutterAutopilot.kt
index ea783eb3..16d58416 100644
--- a/android/src/main/kotlin/com/airship/flutter/FlutterAutopilot.kt
+++ b/android/src/main/kotlin/com/airship/flutter/FlutterAutopilot.kt
@@ -29,9 +29,7 @@ class FlutterAutopilot : BaseAutopilot() {
private val appContext: Context
get() = UAirship.getApplicationContext()
- override fun onAirshipReady(airship: UAirship) {
- super.onAirshipReady(airship)
-
+ override fun onReady(context: Context, airship: UAirship) {
Log.i("FlutterAutopilot", "onAirshipReady")
// If running in the background, start the background Isolate
@@ -66,7 +64,6 @@ class FlutterAutopilot : BaseAutopilot() {
}
}
-
companion object {
private val APP_KEY = stringPreferencesKey("app_key")
private val APP_SECRET = stringPreferencesKey("app_secret")
diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
new file mode 100644
index 00000000..40b66d58
--- /dev/null
+++ b/example/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at https://dart.dev/lints.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+ constant_identifier_names: false
+ prefer_const_constructors: false
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index f29076ce..fba1faf5 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -45,6 +45,15 @@ android {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = '17'
+ }
+
buildTypes {
release {
// TODO: Add your own signing config for the release build.
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index b0a48172..6643e163 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -8,6 +8,11 @@
+
+
+
{
+
+ 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/ic_notification.xml b/example/android/app/src/main/res/drawable/ic_notification.xml
new file mode 100644
index 00000000..a8b409b1
--- /dev/null
+++ b/example/android/app/src/main/res/drawable/ic_notification.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/example/android/build.gradle b/example/android/build.gradle
index b37a815b..fbc5d6f9 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id "com.android.application" version "8.2.2" apply false
+ id "com.android.application" version '8.6.1' apply false
id "org.jetbrains.kotlin.android" version "1.9.0" apply false
id "com.google.gms.google-services" version "4.3.14" apply false
}
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index b4c0361d..8f9af135 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Jul 03 16:09:47 PDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index f953ebb5..cd268768 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -18,7 +18,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
- id "com.android.application" version "8.2.2" apply false
+ id "com.android.application" version '8.6.1' apply false
id "org.jetbrains.kotlin.android" version "1.9.0" apply false
}
diff --git a/example/ios/AirshipExampleNotificationServiceExtension/Info.plist b/example/ios/AirshipExampleNotificationServiceExtension/Info.plist
new file mode 100644
index 00000000..57421ebf
--- /dev/null
+++ b/example/ios/AirshipExampleNotificationServiceExtension/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.usernotifications.service
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).NotificationService
+
+
+
diff --git a/example/ios/AirshipExampleNotificationServiceExtension/NotificationService.swift b/example/ios/AirshipExampleNotificationServiceExtension/NotificationService.swift
new file mode 100644
index 00000000..36b17ca8
--- /dev/null
+++ b/example/ios/AirshipExampleNotificationServiceExtension/NotificationService.swift
@@ -0,0 +1,6 @@
+/* Copyright Airship and Contributors */
+
+import UserNotifications
+import AirshipServiceExtension
+
+class NotificationService: UANotificationServiceExtension {}
diff --git a/example/ios/ExampleWidgets/Assets.xcassets/AccentColor.colorset/Contents.json b/example/ios/ExampleWidgets/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/example/ios/ExampleWidgets/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/example/ios/ExampleWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/ExampleWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..23058801
--- /dev/null
+++ b/example/ios/ExampleWidgets/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/ExampleWidgets/Assets.xcassets/Contents.json b/example/ios/ExampleWidgets/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/example/ios/ExampleWidgets/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/example/ios/ExampleWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json b/example/ios/ExampleWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/example/ios/ExampleWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/example/ios/ExampleWidgets/ExampleWidgetsAttributes.swift b/example/ios/ExampleWidgets/ExampleWidgetsAttributes.swift
new file mode 100644
index 00000000..632913f8
--- /dev/null
+++ b/example/ios/ExampleWidgets/ExampleWidgetsAttributes.swift
@@ -0,0 +1,13 @@
+/* Copyright Airship and Contributors */
+
+import ActivityKit
+
+struct ExampleWidgetsAttributes: 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/ExampleWidgets/ExampleWidgetsBundle.swift b/example/ios/ExampleWidgets/ExampleWidgetsBundle.swift
new file mode 100644
index 00000000..724ea47e
--- /dev/null
+++ b/example/ios/ExampleWidgets/ExampleWidgetsBundle.swift
@@ -0,0 +1,11 @@
+/* Copyright Airship and Contributors */
+
+import WidgetKit
+import SwiftUI
+
+@main
+struct ExampleWidgetsBundle: WidgetBundle {
+ var body: some Widget {
+ ExampleWidgetsLiveActivity()
+ }
+}
diff --git a/example/ios/ExampleWidgets/ExampleWidgetsLiveActivity.swift b/example/ios/ExampleWidgets/ExampleWidgetsLiveActivity.swift
new file mode 100644
index 00000000..2e8e873b
--- /dev/null
+++ b/example/ios/ExampleWidgets/ExampleWidgetsLiveActivity.swift
@@ -0,0 +1,66 @@
+/* Copyright Airship and Contributors */
+
+import ActivityKit
+import WidgetKit
+import SwiftUI
+
+
+struct ExampleWidgetsLiveActivity: Widget {
+ var body: some WidgetConfiguration {
+ ActivityConfiguration(for: ExampleWidgetsAttributes.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 ExampleWidgetsAttributes {
+ fileprivate static var preview: ExampleWidgetsAttributes {
+ ExampleWidgetsAttributes(name: "World")
+ }
+}
+
+extension ExampleWidgetsAttributes.ContentState {
+ fileprivate static var smiley: ExampleWidgetsAttributes.ContentState {
+ ExampleWidgetsAttributes.ContentState(emoji: "😀")
+ }
+
+ fileprivate static var starEyes: ExampleWidgetsAttributes.ContentState {
+ ExampleWidgetsAttributes.ContentState(emoji: "🤩")
+ }
+}
+
+#Preview("Notification", as: .content, using: ExampleWidgetsAttributes.preview) {
+ ExampleWidgetsLiveActivity()
+} contentStates: {
+ ExampleWidgetsAttributes.ContentState.smiley
+ ExampleWidgetsAttributes.ContentState.starEyes
+}
diff --git a/example/ios/ExampleWidgets/Info.plist b/example/ios/ExampleWidgets/Info.plist
new file mode 100644
index 00000000..0f118fb7
--- /dev/null
+++ b/example/ios/ExampleWidgets/Info.plist
@@ -0,0 +1,11 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.widgetkit-extension
+
+
+
diff --git a/example/ios/Podfile b/example/ios/Podfile
index f17bddc9..2580de97 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-platform :ios, '15.0'
+platform :ios, '14.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -39,3 +39,9 @@ post_install do |installer|
flutter_additional_ios_build_settings(target)
end
end
+
+target 'AirshipExampleNotificationServiceExtension' do
+ use_frameworks!
+ use_modular_headers!
+ pod 'AirshipServiceExtension'
+end
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index 8217437a..f945f155 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -8,14 +8,43 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 17E3C866CA3FCD6568C3185B /* Pods_AirshipExampleNotificationServiceExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 060DD0FFEC6F473C0C1B1B41 /* Pods_AirshipExampleNotificationServiceExtension.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- A5B8E4E55C3DA257D8B2783B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EBAC940BA18FD7F92E459C9 /* Pods_Runner.framework */; };
+ 99041B732CC1BD3400031558 /* AirshipPluginExtender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99041B722CC1BD3400031558 /* AirshipPluginExtender.swift */; };
+ 99041C022CC1DAE500031558 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 99041C012CC1DAE500031558 /* WidgetKit.framework */; };
+ 99041C042CC1DAE500031558 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 99041C032CC1DAE500031558 /* SwiftUI.framework */; };
+ 99041C072CC1DAE500031558 /* ExampleWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99041C062CC1DAE500031558 /* ExampleWidgetsBundle.swift */; };
+ 99041C092CC1DAE500031558 /* ExampleWidgetsLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99041C082CC1DAE500031558 /* ExampleWidgetsLiveActivity.swift */; };
+ 99041C0F2CC1DAE600031558 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 99041C0E2CC1DAE600031558 /* Assets.xcassets */; };
+ 99041C132CC1DAE600031558 /* ExampleWidgetsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 99041C002CC1DAE500031558 /* ExampleWidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 99041C1B2CC1DB2900031558 /* ExampleWidgetsAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99041C192CC1DB1D00031558 /* ExampleWidgetsAttributes.swift */; };
+ 99041C232CC29E0300031558 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99041C222CC29E0300031558 /* NotificationService.swift */; };
+ 99041C272CC29E0300031558 /* AirshipExampleNotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 99041C202CC29E0300031558 /* AirshipExampleNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 991A20A62CC5807D0025F15C /* ExampleWidgetsAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99041C192CC1DB1D00031558 /* ExampleWidgetsAttributes.swift */; };
+ F2752F00AE0F784D37990F4D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB5811C302D62694114284C7 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 99041C112CC1DAE600031558 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 99041BFF2CC1DAE500031558;
+ remoteInfo = ExampleWidgetsExtension;
+ };
+ 99041C252CC29E0300031558 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 99041C1F2CC29E0300031558;
+ remoteInfo = AirshipExampleNotificationServiceExtension;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -27,19 +56,33 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
+ 99041C182CC1DAE600031558 /* Embed Foundation Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ 99041C132CC1DAE600031558 /* ExampleWidgetsExtension.appex in Embed Foundation Extensions */,
+ 99041C272CC29E0300031558 /* AirshipExampleNotificationServiceExtension.appex in Embed Foundation Extensions */,
+ );
+ name = "Embed Foundation Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 060DD0FFEC6F473C0C1B1B41 /* Pods_AirshipExampleNotificationServiceExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AirshipExampleNotificationServiceExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 22ED3F085B82D96DD685CBE2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
- 3EBAC940BA18FD7F92E459C9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
45277AEA23C53FC5002AEA21 /* Airship.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Airship.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 49DAB7D490EA653F2FF29FB1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ 55C9286D2A474BF41F18325D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
- 8259AB33CF8B567B4B9BD8AA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 7B0A7B4D0A9B4489842A61EB /* Pods-AirshipExampleNotificationServiceExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirshipExampleNotificationServiceExtension.profile.xcconfig"; path = "Target Support Files/Pods-AirshipExampleNotificationServiceExtension/Pods-AirshipExampleNotificationServiceExtension.profile.xcconfig"; sourceTree = ""; };
+ 96EFF562C1F44C0363D9859D /* Pods-AirshipExampleNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirshipExampleNotificationServiceExtension.debug.xcconfig"; path = "Target Support Files/Pods-AirshipExampleNotificationServiceExtension/Pods-AirshipExampleNotificationServiceExtension.debug.xcconfig"; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -47,8 +90,22 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- D98F489A26588C2B01B017D9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 99041B722CC1BD3400031558 /* AirshipPluginExtender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AirshipPluginExtender.swift; sourceTree = ""; };
+ 99041C002CC1DAE500031558 /* ExampleWidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ExampleWidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ 99041C012CC1DAE500031558 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
+ 99041C032CC1DAE500031558 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
+ 99041C062CC1DAE500031558 /* ExampleWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleWidgetsBundle.swift; sourceTree = ""; };
+ 99041C082CC1DAE500031558 /* ExampleWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleWidgetsLiveActivity.swift; sourceTree = ""; };
+ 99041C0E2CC1DAE600031558 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 99041C102CC1DAE600031558 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 99041C192CC1DB1D00031558 /* ExampleWidgetsAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleWidgetsAttributes.swift; sourceTree = ""; };
+ 99041C202CC29E0300031558 /* AirshipExampleNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AirshipExampleNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ 99041C222CC29E0300031558 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; };
+ 99041C242CC29E0300031558 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ AB5811C302D62694114284C7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DF39D0BC24EC8388007487C4 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
+ F5C980E6DA765A595DF7C726 /* Pods-AirshipExampleNotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirshipExampleNotificationServiceExtension.release.xcconfig"; path = "Target Support Files/Pods-AirshipExampleNotificationServiceExtension/Pods-AirshipExampleNotificationServiceExtension.release.xcconfig"; sourceTree = ""; };
+ F80EA22C38488422A05FF96F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -56,7 +113,24 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- A5B8E4E55C3DA257D8B2783B /* Pods_Runner.framework in Frameworks */,
+ F2752F00AE0F784D37990F4D /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 99041BFD2CC1DAE500031558 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 99041C042CC1DAE500031558 /* SwiftUI.framework in Frameworks */,
+ 99041C022CC1DAE500031558 /* WidgetKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 99041C1D2CC29E0300031558 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 17E3C866CA3FCD6568C3185B /* Pods_AirshipExampleNotificationServiceExtension.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -67,7 +141,10 @@
isa = PBXGroup;
children = (
45277AEA23C53FC5002AEA21 /* Airship.framework */,
- 3EBAC940BA18FD7F92E459C9 /* Pods_Runner.framework */,
+ AB5811C302D62694114284C7 /* Pods_Runner.framework */,
+ 99041C012CC1DAE500031558 /* WidgetKit.framework */,
+ 99041C032CC1DAE500031558 /* SwiftUI.framework */,
+ 060DD0FFEC6F473C0C1B1B41 /* Pods_AirshipExampleNotificationServiceExtension.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -75,9 +152,12 @@
4A5F3BC20F2F71680F0EA95B /* Pods */ = {
isa = PBXGroup;
children = (
- 49DAB7D490EA653F2FF29FB1 /* Pods-Runner.debug.xcconfig */,
- 8259AB33CF8B567B4B9BD8AA /* Pods-Runner.release.xcconfig */,
- D98F489A26588C2B01B017D9 /* Pods-Runner.profile.xcconfig */,
+ 55C9286D2A474BF41F18325D /* Pods-Runner.debug.xcconfig */,
+ F80EA22C38488422A05FF96F /* Pods-Runner.release.xcconfig */,
+ 22ED3F085B82D96DD685CBE2 /* Pods-Runner.profile.xcconfig */,
+ 96EFF562C1F44C0363D9859D /* Pods-AirshipExampleNotificationServiceExtension.debug.xcconfig */,
+ F5C980E6DA765A595DF7C726 /* Pods-AirshipExampleNotificationServiceExtension.release.xcconfig */,
+ 7B0A7B4D0A9B4489842A61EB /* Pods-AirshipExampleNotificationServiceExtension.profile.xcconfig */,
);
path = Pods;
sourceTree = "";
@@ -98,6 +178,8 @@
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
+ 99041C052CC1DAE500031558 /* ExampleWidgets */,
+ 99041C212CC29E0300031558 /* AirshipExampleNotificationServiceExtension */,
97C146EF1CF9000F007C117D /* Products */,
4A5F3BC20F2F71680F0EA95B /* Pods */,
18E3DDFE37410BE22FF694C3 /* Frameworks */,
@@ -108,6 +190,8 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
+ 99041C002CC1DAE500031558 /* ExampleWidgetsExtension.appex */,
+ 99041C202CC29E0300031558 /* AirshipExampleNotificationServiceExtension.appex */,
);
name = Products;
sourceTree = "";
@@ -123,6 +207,7 @@
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 99041B722CC1BD3400031558 /* AirshipPluginExtender.swift */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
@@ -136,6 +221,27 @@
name = "Supporting Files";
sourceTree = "";
};
+ 99041C052CC1DAE500031558 /* ExampleWidgets */ = {
+ isa = PBXGroup;
+ children = (
+ 99041C062CC1DAE500031558 /* ExampleWidgetsBundle.swift */,
+ 99041C082CC1DAE500031558 /* ExampleWidgetsLiveActivity.swift */,
+ 99041C192CC1DB1D00031558 /* ExampleWidgetsAttributes.swift */,
+ 99041C0E2CC1DAE600031558 /* Assets.xcassets */,
+ 99041C102CC1DAE600031558 /* Info.plist */,
+ );
+ path = ExampleWidgets;
+ sourceTree = "";
+ };
+ 99041C212CC29E0300031558 /* AirshipExampleNotificationServiceExtension */ = {
+ isa = PBXGroup;
+ children = (
+ 99041C222CC29E0300031558 /* NotificationService.swift */,
+ 99041C242CC29E0300031558 /* Info.plist */,
+ );
+ path = AirshipExampleNotificationServiceExtension;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -143,30 +249,70 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 92A7554725E28B35CD26D4E6 /* [CP] Check Pods Manifest.lock */,
+ 1E091F8DEFEE6AA058F231B9 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
+ 99041C182CC1DAE600031558 /* Embed Foundation Extensions */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 59437A90479CED8BD3D53907 /* [CP] Embed Pods Frameworks */,
+ 762FAAF0484562820949CC92 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
+ 99041C122CC1DAE600031558 /* PBXTargetDependency */,
+ 99041C262CC29E0300031558 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
+ 99041BFF2CC1DAE500031558 /* ExampleWidgetsExtension */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 99041C142CC1DAE600031558 /* Build configuration list for PBXNativeTarget "ExampleWidgetsExtension" */;
+ buildPhases = (
+ 99041BFC2CC1DAE500031558 /* Sources */,
+ 99041BFD2CC1DAE500031558 /* Frameworks */,
+ 99041BFE2CC1DAE500031558 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ExampleWidgetsExtension;
+ productName = ExampleWidgetsExtension;
+ productReference = 99041C002CC1DAE500031558 /* ExampleWidgetsExtension.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
+ 99041C1F2CC29E0300031558 /* AirshipExampleNotificationServiceExtension */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 99041C282CC29E0300031558 /* Build configuration list for PBXNativeTarget "AirshipExampleNotificationServiceExtension" */;
+ buildPhases = (
+ FEFEACE62CF044E69FD585AA /* [CP] Check Pods Manifest.lock */,
+ 99041C1C2CC29E0300031558 /* Sources */,
+ 99041C1D2CC29E0300031558 /* Frameworks */,
+ 99041C1E2CC29E0300031558 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = AirshipExampleNotificationServiceExtension;
+ productName = AirshipExampleNotificationServiceExtension;
+ productReference = 99041C202CC29E0300031558 /* AirshipExampleNotificationServiceExtension.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
+ DefaultBuildSystemTypeForWorkspace = Latest;
+ LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
@@ -175,6 +321,12 @@
DevelopmentTeam = PGJV57GD94;
LastSwiftMigration = 1120;
};
+ 99041BFF2CC1DAE500031558 = {
+ CreatedOnToolsVersion = 15.4;
+ };
+ 99041C1F2CC29E0300031558 = {
+ CreatedOnToolsVersion = 15.4;
+ };
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
@@ -191,6 +343,8 @@
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
+ 99041C1F2CC29E0300031558 /* AirshipExampleNotificationServiceExtension */,
+ 99041BFF2CC1DAE500031558 /* ExampleWidgetsExtension */,
);
};
/* End PBXProject section */
@@ -207,9 +361,46 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 99041BFE2CC1DAE500031558 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 99041C0F2CC1DAE600031558 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 99041C1E2CC29E0300031558 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 1E091F8DEFEE6AA058F231B9 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /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;
+ };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -226,7 +417,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
};
- 59437A90479CED8BD3D53907 /* [CP] Embed Pods Frameworks */ = {
+ 762FAAF0484562820949CC92 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -236,54 +427,56 @@
"${BUILT_PRODUCTS_DIR}/Airship/AirshipKit.framework",
"${BUILT_PRODUCTS_DIR}/AirshipFrameworkProxy/AirshipFrameworkProxy.framework",
"${BUILT_PRODUCTS_DIR}/airship_flutter/airship_flutter.framework",
+ "${BUILT_PRODUCTS_DIR}/AirshipServiceExtension/AirshipServiceExtension.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AirshipKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AirshipFrameworkProxy.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/airship_flutter.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AirshipServiceExtension.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 92A7554725E28B35CD26D4E6 /* [CP] Check Pods Manifest.lock */ = {
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
- inputFileListPaths = (
- );
inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputFileListPaths = (
);
+ name = "Run Script";
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /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;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
- 9740EEB61CF901F6004384FC /* Run Script */ = {
+ FEFEACE62CF044E69FD585AA /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
- alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
);
- name = "Run Script";
outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-AirshipExampleNotificationServiceExtension-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /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 */
@@ -292,13 +485,46 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 991A20A62CC5807D0025F15C /* ExampleWidgetsAttributes.swift in Sources */,
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ 99041B732CC1BD3400031558 /* AirshipPluginExtender.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 99041BFC2CC1DAE500031558 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 99041C092CC1DAE500031558 /* ExampleWidgetsLiveActivity.swift in Sources */,
+ 99041C1B2CC1DB2900031558 /* ExampleWidgetsAttributes.swift in Sources */,
+ 99041C072CC1DAE500031558 /* ExampleWidgetsBundle.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 99041C1C2CC29E0300031558 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 99041C232CC29E0300031558 /* NotificationService.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 99041C122CC1DAE600031558 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 99041BFF2CC1DAE500031558 /* ExampleWidgetsExtension */;
+ targetProxy = 99041C112CC1DAE600031558 /* PBXContainerItemProxy */;
+ };
+ 99041C262CC29E0300031558 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 99041C1F2CC29E0300031558 /* AirshipExampleNotificationServiceExtension */;
+ targetProxy = 99041C252CC29E0300031558 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -361,7 +587,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -373,6 +599,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -383,7 +610,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -448,7 +675,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -498,7 +725,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
@@ -511,6 +738,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
@@ -522,7 +750,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -546,6 +774,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
@@ -557,7 +786,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -575,6 +804,247 @@
};
name = Release;
};
+ 99041C152CC1DAE600031558 /* 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_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ 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 = ExampleWidgets/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = ExampleWidgets;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 The Chromium Authors. All rights reserved.";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.5;
+ 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.ExampleWidgets;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 99041C162CC1DAE600031558 /* 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_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ 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 = ExampleWidgets/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = ExampleWidgets;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 The Chromium Authors. All rights reserved.";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.5;
+ 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.ExampleWidgets;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 99041C172CC1DAE600031558 /* Profile */ = {
+ 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_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ 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 = ExampleWidgets/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = ExampleWidgets;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 The Chromium Authors. All rights reserved.";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.5;
+ 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.ExampleWidgets;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Profile;
+ };
+ 99041C292CC29E0300031558 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 96EFF562C1F44C0363D9859D /* Pods-AirshipExampleNotificationServiceExtension.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ 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 = AirshipExampleNotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = AirshipExampleNotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 The Chromium Authors. All rights reserved.";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.5;
+ 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.AirshipExampleNotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 99041C2A2CC29E0300031558 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F5C980E6DA765A595DF7C726 /* Pods-AirshipExampleNotificationServiceExtension.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ 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 = AirshipExampleNotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = AirshipExampleNotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 The Chromium Authors. All rights reserved.";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.5;
+ 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.AirshipExampleNotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 99041C2B2CC29E0300031558 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7B0A7B4D0A9B4489842A61EB /* Pods-AirshipExampleNotificationServiceExtension.profile.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ 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 = AirshipExampleNotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = AirshipExampleNotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 The Chromium Authors. All rights reserved.";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.5;
+ 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.AirshipExampleNotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Profile;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -598,6 +1068,26 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 99041C142CC1DAE600031558 /* Build configuration list for PBXNativeTarget "ExampleWidgetsExtension" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 99041C152CC1DAE600031558 /* Debug */,
+ 99041C162CC1DAE600031558 /* Release */,
+ 99041C172CC1DAE600031558 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 99041C282CC29E0300031558 /* Build configuration list for PBXNativeTarget "AirshipExampleNotificationServiceExtension" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 99041C292CC29E0300031558 /* Debug */,
+ 99041C2A2CC29E0300031558 /* Release */,
+ 99041C2B2CC29E0300031558 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
diff --git a/example/ios/Runner/AirshipPluginExtender.swift b/example/ios/Runner/AirshipPluginExtender.swift
new file mode 100644
index 00000000..00ea2c95
--- /dev/null
+++ b/example/ios/Runner/AirshipPluginExtender.swift
@@ -0,0 +1,27 @@
+/* Copyright Airship and Contributors */
+
+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/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift
index 9da429a9..7039186a 100644
--- a/example/ios/Runner/AppDelegate.swift
+++ b/example/ios/Runner/AppDelegate.swift
@@ -1,8 +1,10 @@
+/* Copyright Airship and Contributors */
+
import UIKit
import Flutter
import AirshipKit
-@UIApplicationMain
+@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
index 3b590425..41e5284d 100644
--- a/example/ios/Runner/Info.plist
+++ b/example/ios/Runner/Info.plist
@@ -50,5 +50,7 @@
UIApplicationSupportsIndirectInputEvents
+ NSSupportsLiveActivities
+
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 0eb1ff40..7024aebc 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -8,6 +8,7 @@ import 'package:airship_example/styles.dart';
import 'package:flutter/services.dart' show DeviceOrientation, SystemChrome;
import 'package:airship_example/screens/home.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
// Supported deep links
@@ -28,6 +29,10 @@ void main() {
]);
var config = AirshipConfig(
+ androidConfig: AndroidConfig(
+ notificationConfig: AndroidNotificationConfig(
+ icon: "ic_notification",
+ )),
defaultEnvironment: ConfigEnvironment(
appKey: "APP_KEY",
appSecret: "APP_SECRET",
@@ -50,7 +55,10 @@ void main() {
}
class MyApp extends StatefulWidget {
+ const MyApp({super.key});
+
@override
+ // ignore: library_private_types_in_public_api
_MyAppState createState() => _MyAppState();
}
@@ -146,7 +154,7 @@ class _MyAppState extends State with SingleTickerProviderStateMixin {
Airship.messageCenter.onDisplay.listen((event) {
key.currentState
- ?.push(MaterialPageRoute(builder: (BuildContext context) {
+ ?.push(MaterialPageRoute(builder: (BuildContext context) {
return event.messageId != null
? MessageView(
messageId: event.messageId ?? "",
@@ -197,7 +205,7 @@ class _MyAppState extends State with SingleTickerProviderStateMixin {
unselectedLabelColor: Colors.grey, // Set unselected label color
labelColor:
Styles.airshipBlue, // Set selected label color to airshipBlue
- tabs: [
+ tabs: const [
Tab(
icon: Icon(Icons.home),
),
@@ -221,17 +229,18 @@ class _MyAppState extends State with SingleTickerProviderStateMixin {
Widget tabBarView() {
return PopScope(
+ // ignore: deprecated_member_use
onPopInvoked: null,
child: Scaffold(
backgroundColor: Styles.borders,
body: TabBarView(
- children: [
+ controller: controller,
+ children: const [
Home(),
MessageCenter(),
PreferenceCenter(),
Settings()
],
- controller: controller,
),
bottomNavigationBar: bottomNavigationBar(),
),
diff --git a/example/lib/screens/home.dart b/example/lib/screens/home.dart
index 8da2d8fc..2495a63c 100644
--- a/example/lib/screens/home.dart
+++ b/example/lib/screens/home.dart
@@ -1,14 +1,19 @@
import 'package:flutter/material.dart';
import 'package:airship_example/styles.dart';
import 'package:airship_example/widgets/notifications_enabled_button.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
+import 'dart:io' show Platform;
+import 'package:uuid/uuid.dart';
class Home extends StatefulWidget {
+ const Home({super.key});
+
@override
- _HomeState createState() => _HomeState();
+ HomeState createState() => HomeState();
}
-class _HomeState extends State {
+class HomeState extends State {
@override
void initState() {
initAirshipListeners();
@@ -26,6 +31,123 @@ class _HomeState extends State {
});
}
+ Future _startNewActivity() async {
+ if (Platform.isIOS) {
+ LiveActivityStartRequest startRequest = LiveActivityStartRequest(
+ attributesType: 'ExampleWidgetsAttributes',
+ attributes: {
+ "name": Uuid().v4(),
+ },
+ content:
+ LiveActivityContent(state: {'emoji': '🙌'}, relevanceScore: 0.0));
+
+ await Airship.liveActivityManager.start(startRequest);
+ } else if (Platform.isAndroid) {
+ LiveUpdateStartRequest createRequest = LiveUpdateStartRequest(
+ name: "Cool",
+ type: 'Example',
+ content: {'emoji': '🙌'},
+ );
+
+ await Airship.liveUpdateManager.start(createRequest);
+ }
+ }
+
+ Future _stopAllActivities() async {
+ if (Platform.isIOS) {
+ List activities =
+ await Airship.liveActivityManager.listAll();
+ for (var activity in activities) {
+ LiveActivityStopRequest stopRequest = LiveActivityStopRequest(
+ attributesType: 'ExampleWidgetsAttributes',
+ activityId: activity.id,
+ dismissalPolicy: LiveActivityDismissalPolicyImmediate(),
+ );
+
+ await Airship.liveActivityManager.end(stopRequest);
+ }
+ } else if (Platform.isAndroid) {
+ List updates = await Airship.liveUpdateManager.listAll();
+ for (var update in updates) {
+ LiveUpdateEndRequest stopRequest =
+ LiveUpdateEndRequest(name: update.name);
+ await Airship.liveUpdateManager.end(stopRequest);
+ }
+ }
+ }
+
+ Future _updateAllActivities() async {
+ if (Platform.isIOS) {
+ List activities =
+ await Airship.liveActivityManager.listAll();
+ for (var activity in activities) {
+ LiveActivityContent content =
+ LiveActivityContent(state: {'emoji': '🙌'}, relevanceScore: 0.0);
+
+ LiveActivityUpdateRequest updateRequest = LiveActivityUpdateRequest(
+ attributesType: 'ExampleWidgetsAttributes',
+ activityId: activity.id,
+ content: content,
+ );
+
+ await Airship.liveActivityManager.update(updateRequest);
+ }
+ } else if (Platform.isAndroid) {
+ List updates = await Airship.liveUpdateManager.listAll();
+
+ for (var update in updates) {
+ var currentEmoji = update.content['emoji'] ?? '';
+
+ LiveUpdateUpdateRequest request = LiveUpdateUpdateRequest(
+ name: update.name,
+ content: {
+ 'emoji': currentEmoji == '🙌' ? '👍' : '🙌',
+ },
+ );
+
+ await Airship.liveUpdateManager.update(request);
+ }
+ }
+ }
+
+ Widget _buildTableRow(
+ String label, String buttonText, VoidCallback onPressed) {
+ return Container(
+ decoration: BoxDecoration(
+ color: Colors.grey.withAlpha(5), // Slightly lighter than the background
+ borderRadius: BorderRadius.circular(12),
+ ),
+ padding: EdgeInsets.symmetric(vertical: 8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(left: 16), // Offset label by 16 points
+ child: Text(
+ label,
+ style: TextStyle(color: Colors.white),
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.only(
+ right: 16), // Padding button 16 points from the right
+ child: SizedBox(
+ width: 100, // Fixed button width for uniform size
+ child: ElevatedButton(
+ onPressed: onPressed,
+ style: ElevatedButton.styleFrom(
+ foregroundColor: Colors.white,
+ backgroundColor: Styles.airshipBlue.withAlpha(80),
+ ),
+ child: Text(buttonText),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -49,7 +171,11 @@ class _HomeState extends State {
enableNotificationsButton =
Center(child: NotificationsEnabledButton(
onPressed: () {
- Airship.push.setUserNotificationsEnabled(true);
+ Airship.push.enableUserNotifications(
+ options: EnableUserPushNotificationsArgs(
+ fallback: PromptPermissionFallback.systemSettings,
+ ));
+
setState(() {});
},
));
@@ -71,6 +197,38 @@ class _HomeState extends State {
},
),
),
+ SizedBox(height: 36),
+ Center(
+ child: Card(
+ color: Colors.grey.withAlpha(15),
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ Platform.isIOS ? 'Live Activities' : 'Live Updates',
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ color: Colors.white,
+ ),
+ ),
+ SizedBox(height: 12),
+ Column(
+ children: [
+ _buildTableRow(
+ 'Start New', 'Start', _startNewActivity),
+ _buildTableRow(
+ 'End All', 'End', _stopAllActivities),
+ _buildTableRow(
+ 'Update All', 'Update', _updateAllActivities),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
]),
),
));
diff --git a/example/lib/screens/message_center.dart b/example/lib/screens/message_center.dart
index 4b4ac22f..50b7d240 100644
--- a/example/lib/screens/message_center.dart
+++ b/example/lib/screens/message_center.dart
@@ -1,14 +1,17 @@
import 'package:flutter/material.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
import 'package:airship_example/styles.dart';
import 'package:airship_example/screens/message_view.dart';
class MessageCenter extends StatefulWidget {
+ const MessageCenter({super.key});
+
@override
- _MessageCenterState createState() => _MessageCenterState();
+ MessageCenterState createState() => MessageCenterState();
}
-class _MessageCenterState extends State {
+class MessageCenterState extends State {
@override
void initState() {
initAirshipListeners();
@@ -31,7 +34,7 @@ class _MessageCenterState extends State {
@override
Widget build(BuildContext context) {
- Widget _buildMessageList(final List messages) {
+ Widget buildMessageList(final List messages) {
return RefreshIndicator(
strokeWidth: 1,
displacement: 50,
@@ -54,13 +57,12 @@ class _MessageCenterState extends State {
// Add stream to check isRead
child: ListTile(
title: message.isRead
- ? Text('${message.title}')
- : Text('${message.title}',
+ ? Text(message.title)
+ : Text(message.title,
style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text('${message.sentDate}'),
- leading: Icon(message.isRead
- ? Icons.check_circle
- : Icons.markunread),
+ leading: Icon(
+ message.isRead ? Icons.check_circle : Icons.markunread),
onTap: () {
Navigator.push(
context,
@@ -94,7 +96,7 @@ class _MessageCenterState extends State {
bottom: false,
child: Column(
children: [
- Expanded(child: _buildMessageList(list)),
+ Expanded(child: buildMessageList(list)),
],
),
);
diff --git a/example/lib/screens/message_view.dart b/example/lib/screens/message_view.dart
index 0f5689bd..afc66705 100644
--- a/example/lib/screens/message_view.dart
+++ b/example/lib/screens/message_view.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
import 'package:airship_example/styles.dart';
import 'package:flutter/services.dart';
@@ -7,13 +8,13 @@ import 'package:collection/collection.dart';
class MessageView extends StatefulWidget {
final String messageId;
- MessageView({required this.messageId});
+ const MessageView({super.key, required this.messageId});
@override
- _MessageViewState createState() => _MessageViewState();
+ MessageViewState createState() => MessageViewState();
}
-class _MessageViewState extends State {
+class MessageViewState extends State {
bool isLoading = true;
@override
@@ -38,7 +39,7 @@ class _MessageViewState extends State {
return Scaffold(
appBar: AppBar(
- title: message != null ? Text("${message.title}") : Container(),
+ title: message != null ? Text(message.title) : Container(),
backgroundColor: Styles.background,
),
body: Stack(children: [
@@ -82,7 +83,7 @@ class _MessageViewState extends State {
builder: (context) => AlertDialog(
title: Text(
e.message != null ? e.message! : "Unable to load message"),
- content: Text(e.details != null ? e.details : ""),
+ content: Text(e.details ?? ""),
));
});
}
diff --git a/example/lib/screens/named_user_add.dart b/example/lib/screens/named_user_add.dart
index efd6c0a1..c2952e19 100644
--- a/example/lib/screens/named_user_add.dart
+++ b/example/lib/screens/named_user_add.dart
@@ -1,58 +1,51 @@
import 'package:flutter/material.dart';
import 'package:airship_example/styles.dart';
import 'package:airship_example/widgets/text_add_bar.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
class NamedUserAdd extends StatefulWidget {
- final updateParent;
+ final VoidCallback updateParent;
- NamedUserAdd({this.updateParent});
+ const NamedUserAdd({Key? key, required this.updateParent}) : super(key: key);
@override
- _NamedUserAddState createState() =>
- _NamedUserAddState(updateParent: updateParent);
+ NamedUserAddState createState() => NamedUserAddState();
}
-class _NamedUserAddState extends State {
- final updateParent;
-
- _NamedUserAddState({this.updateParent});
-
+class NamedUserAddState extends State {
@override
void initState() {
- Airship.analytics.trackScreen('Add Named User');
super.initState();
+ Airship.analytics.trackScreen('Add Named User');
}
@override
Widget build(BuildContext context) {
return Scaffold(
- appBar: AppBar(
- title: Text("Add Named User"),
- backgroundColor: Styles.background,
- ),
- body: FutureBuilder(
- future: Airship.contact.namedUserId,
- builder: (context, snapshot) {
- return SafeArea(
- bottom: false,
- child: Column(
- children: [
- Padding(
- padding: const EdgeInsets.all(12.0),
- child: TextAddBar(
- label: snapshot.hasData ? snapshot.data! : "Not set",
- onTap: (text) {
- Airship.contact.identify(text);
- updateParent();
- Navigator.pop(context);
- },
- ),
- ),
- ],
+ appBar: AppBar(
+ title: const Text("Add Named User"),
+ backgroundColor: Styles.background,
+ ),
+ body: FutureBuilder(
+ future: Airship.contact.namedUserId,
+ builder: (context, snapshot) {
+ return SafeArea(
+ bottom: false,
+ child: Padding(
+ padding: const EdgeInsets.all(12.0),
+ child: TextAddBar(
+ label: snapshot.data ?? "Not set",
+ onTap: (text) {
+ Airship.contact.identify(text);
+ widget.updateParent();
+ Navigator.pop(context);
+ },
),
- );
- },
- ));
+ ),
+ );
+ },
+ ),
+ );
}
}
diff --git a/example/lib/screens/preference_center.dart b/example/lib/screens/preference_center.dart
index 382e736c..695db819 100644
--- a/example/lib/screens/preference_center.dart
+++ b/example/lib/screens/preference_center.dart
@@ -1,13 +1,16 @@
import 'package:flutter/material.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
import 'package:flutter_section_list/flutter_section_list.dart';
class PreferenceCenter extends StatefulWidget {
+ const PreferenceCenter({super.key});
+
@override
- _PreferenceCenterState createState() => _PreferenceCenterState();
+ PreferenceCenterState createState() => PreferenceCenterState();
}
-class _PreferenceCenterState extends State
+class PreferenceCenterState extends State
with SectionAdapterMixin {
String preferenceCenterId = "neat";
PreferenceCenterConfig? fullPreferenceCenterConfig;
@@ -32,7 +35,9 @@ class _PreferenceCenterState extends State
Airship.preferenceCenter.onDisplay.listen((event) {});
Airship.push.onNotificationStatusChanged.listen((event) {
- setState(() { isOptedInToNotifications = event.status.isOptedIn; });
+ setState(() {
+ isOptedInToNotifications = event.status.isOptedIn;
+ });
});
}
@@ -67,15 +72,15 @@ class _PreferenceCenterState extends State
/// Filtered version of the preference center config based on the conditions
/// defined by sections and items.
PreferenceCenterConfig? get preferenceCenterConfig {
- var state = new PreferenceCenterConditionState(isOptedInToNotifications);
+ var state = PreferenceCenterConditionState(isOptedInToNotifications);
if (fullPreferenceCenterConfig == null) return null;
- var sections = fullPreferenceCenterConfig!.sections.where((s) =>
- s.evaluateConditions(state)
- ).map((s) => s.copy(
- s.items?.where((i) => i.evaluateConditions(state)).toList() ?? []
- )).toList();
+ var sections = fullPreferenceCenterConfig!.sections
+ .where((s) => s.evaluateConditions(state))
+ .map((s) => s.copy(
+ s.items?.where((i) => i.evaluateConditions(state)).toList() ?? []))
+ .toList();
return fullPreferenceCenterConfig!.copy(sections);
}
@@ -99,8 +104,9 @@ class _PreferenceCenterState extends State
} else {
return false;
}
- } else
+ } else {
return false;
+ }
}
void onPreferenceChannelItemToggled(String subscriptionId, bool subscribe) {
@@ -119,10 +125,11 @@ class _PreferenceCenterState extends State
void applyContactSubscription(
String subscriptionId, List scopes, bool subscribe) {
List currentScopes =
- activeContactSubscriptions[subscriptionId] ?? List.empty(growable: true);
+ activeContactSubscriptions[subscriptionId] ??
+ List.empty(growable: true);
List newScopes = List.empty(growable: true);
if (subscribe) {
- newScopes = new List.from(currentScopes)..addAll(scopes);
+ newScopes = List.from(currentScopes)..addAll(scopes);
} else {
currentScopes.removeWhere((item) => scopes.contains(item));
newScopes = currentScopes;
@@ -266,11 +273,9 @@ class _PreferenceCenterState extends State
@override
Widget getItem(BuildContext context, IndexPath indexPath) {
- return Container(
- child: Stack(
- alignment: AlignmentDirectional.bottomCenter,
- children: [item(indexPath), Divider(height: 0.5)],
- ),
+ return Stack(
+ alignment: AlignmentDirectional.bottomCenter,
+ children: [item(indexPath), Divider(height: 0.5)],
);
}
@@ -287,9 +292,9 @@ class _PreferenceCenterState extends State
return Container(
color: Colors.blueGrey,
child: ListTile(
- title: Text('${preferenceCenterConfig?.display?.title ?? ''}',
+ title: Text(preferenceCenterConfig?.display?.title ?? '',
style: TextStyle(fontWeight: FontWeight.bold)),
- subtitle: Text('${preferenceCenterConfig?.display?.subtitle ?? ''}'),
+ subtitle: Text(preferenceCenterConfig?.display?.subtitle ?? ''),
),
);
}
@@ -305,10 +310,10 @@ class _PreferenceCenterState extends State
color: Colors.cyan,
child: ListTile(
title: Text(
- '${preferenceCenterConfig?.sections[section].display?.title ?? ''}',
+ preferenceCenterConfig?.sections[section].display?.title ?? '',
style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(
- '${preferenceCenterConfig?.sections[section].display?.subtitle ?? ''}'),
+ preferenceCenterConfig?.sections[section].display?.subtitle ?? ''),
),
);
}
diff --git a/example/lib/screens/settings.dart b/example/lib/screens/settings.dart
index 014e2bc5..d78cf0cb 100644
--- a/example/lib/screens/settings.dart
+++ b/example/lib/screens/settings.dart
@@ -2,14 +2,17 @@ import 'package:flutter/material.dart';
import 'package:airship_example/screens/tag_add.dart';
import 'package:airship_example/screens/named_user_add.dart';
import 'package:airship_example/styles.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
class Settings extends StatefulWidget {
+ const Settings({super.key});
+
@override
- _SettingsState createState() => _SettingsState();
+ SettingsState createState() => SettingsState();
}
-class _SettingsState extends State {
+class SettingsState extends State {
@override
void initState() {
Airship.analytics.trackScreen('Settings');
diff --git a/example/lib/screens/tag_add.dart b/example/lib/screens/tag_add.dart
index fc63b8be..60faa7b4 100644
--- a/example/lib/screens/tag_add.dart
+++ b/example/lib/screens/tag_add.dart
@@ -1,94 +1,88 @@
import 'package:flutter/material.dart';
import 'package:airship_example/styles.dart';
import 'package:airship_example/widgets/text_add_bar.dart';
+// ignore: depend_on_referenced_packages
import 'package:airship_flutter/airship_flutter.dart';
class TagAdd extends StatefulWidget {
- final updateParent;
+ final VoidCallback updateParent;
- TagAdd({this.updateParent});
+ const TagAdd({Key? key, required this.updateParent}) : super(key: key);
@override
- _TagAddState createState() => _TagAddState(updateParent: updateParent);
+ TagAddState createState() => TagAddState();
}
-class _TagAddState extends State {
- final updateParent;
-
- _TagAddState({this.updateParent});
-
+class TagAddState extends State {
@override
void initState() {
- Airship.analytics.trackScreen('Add Tag');
super.initState();
+ Airship.analytics.trackScreen('Add Tag');
}
@override
Widget build(BuildContext context) {
- Widget _buildTagList(List tags) {
- return ListView.builder(
- itemCount: tags.length,
- itemBuilder: (context, index) {
- var tag = tags[index];
-
- return Dismissible(
- key: Key(UniqueKey().toString()),
- background: Container(color: Styles.airshipRed),
- onDismissed: (direction) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text("tag \"$tag\" removed")));
- Airship.channel.removeTags([tag]);
- updateState();
- },
- child: Card(
- elevation: 5.0,
- child: ListTile(
- title: Text('$tag'),
- )),
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text("Add Tag"),
+ backgroundColor: Styles.background,
+ ),
+ body: FutureBuilder>(
+ future: Airship.channel.tags,
+ builder: (context, snapshot) {
+ return SafeArea(
+ bottom: false,
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(12.0),
+ child: TextAddBar(
+ label: "Add a tag",
+ onTap: (tagText) {
+ FocusScope.of(context).unfocus();
+ Airship.channel.addTags([tagText]);
+ _updateState();
+ },
+ ),
+ ),
+ if (snapshot.hasData)
+ Expanded(child: _buildTagList(snapshot.data!))
+ else
+ const SizedBox.shrink(),
+ ],
+ ),
);
},
- );
- }
-
- return Scaffold(
- appBar: AppBar(
- title: Text("Add Tag"),
- backgroundColor: Styles.background,
- ),
- body: FutureBuilder>(
- future: Airship.channel.tags,
- builder: (context, snapshot) {
- Expanded? expandedList;
-
- if (snapshot.hasData) {
- expandedList = Expanded(child: _buildTagList(snapshot.data!));
- }
+ ),
+ );
+ }
- return SafeArea(
- bottom: false,
- child: Column(
- children: [
- Padding(
- padding: const EdgeInsets.all(12.0),
- child: TextAddBar(
- label: "Add a tag",
- onTap: (tagText) {
- FocusScope.of(context).unfocus();
- Airship.channel.addTags([tagText]);
- updateState();
- },
- ),
- ),
- expandedList ?? Container(),
- ],
- ),
+ Widget _buildTagList(List tags) {
+ return ListView.builder(
+ itemCount: tags.length,
+ itemBuilder: (context, index) {
+ final tag = tags[index];
+ return Dismissible(
+ key: ValueKey(tag),
+ background: Container(color: Styles.airshipRed),
+ onDismissed: (_) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('tag "$tag" removed')),
);
+ Airship.channel.removeTags([tag]);
+ _updateState();
},
- ));
+ child: Card(
+ elevation: 5.0,
+ child: ListTile(title: Text(tag)),
+ ),
+ );
+ },
+ );
}
- updateState() {
- updateParent();
+ void _updateState() {
+ widget.updateParent();
setState(() {});
}
}
diff --git a/example/lib/widgets/notifications_enabled_button.dart b/example/lib/widgets/notifications_enabled_button.dart
index ca8b2e13..33ddf069 100644
--- a/example/lib/widgets/notifications_enabled_button.dart
+++ b/example/lib/widgets/notifications_enabled_button.dart
@@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:airship_example/styles.dart';
-typedef void TapCallback(String text);
+typedef TapCallback = void Function(String text);
class NotificationsEnabledButton extends StatelessWidget {
- final onPressed;
+ final VoidCallback onPressed;
- NotificationsEnabledButton({
+ const NotificationsEnabledButton({
+ super.key,
required this.onPressed,
});
@@ -16,15 +17,15 @@ class NotificationsEnabledButton extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: MaterialButton(
- child: Text(
- "Enable Push",
- style: Styles.homeButtonText,
- ),
color: Styles.airshipRed,
shape: StadiumBorder(),
height: 40,
minWidth: 400,
padding: EdgeInsets.symmetric(vertical: 35),
- onPressed: onPressed)));
+ onPressed: onPressed,
+ child: Text(
+ "Enable Push",
+ style: Styles.homeButtonText,
+ ))));
}
}
diff --git a/example/lib/widgets/text_add_bar.dart b/example/lib/widgets/text_add_bar.dart
index 60647dfe..586dde46 100644
--- a/example/lib/widgets/text_add_bar.dart
+++ b/example/lib/widgets/text_add_bar.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:airship_example/styles.dart';
import 'package:flutter/services.dart' show SystemChannels;
-typedef void TapCallback(String text);
+typedef TapCallback = void Function(String text);
class TextAddBar extends StatelessWidget {
final String label;
@@ -11,6 +11,7 @@ class TextAddBar extends StatelessWidget {
final focusNode = FocusNode();
TextAddBar({
+ super.key,
required this.label,
required this.onTap,
});
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index e978a6ab..811368e9 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -8,10 +8,11 @@ environment:
sdk: ">=2.17.2 <4.0.0"
dependencies:
+ uuid: ^3.0.7 # For live activities
flutter:
sdk: flutter
flutter_section_list: ^1.1.1
-
+ collection: ^1.18.0
dev_dependencies:
@@ -20,7 +21,7 @@ dev_dependencies:
airship_flutter:
path: ../
-
+ flutter_lints: ^2.0.0 # Add this line (use the latest version available)
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8b781980..ba1a9330 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Tue Aug 06 11:27:54 PDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/ios/Classes/AirshipAutopilot.swift b/ios/Classes/AirshipAutopilot.swift
index 48e1eba0..25e4075b 100644
--- a/ios/Classes/AirshipAutopilot.swift
+++ b/ios/Classes/AirshipAutopilot.swift
@@ -2,8 +2,6 @@ import Flutter
import AirshipKit
import AirshipFrameworkProxy
-
-@objc(FlutterAirshipAutopilot)
public class AirshipAutopilot: NSObject {
@objc
diff --git a/ios/Classes/AirshipPlugin.m b/ios/Classes/AirshipPlugin.m
index 3d7e05f5..f0e7bdf7 100644
--- a/ios/Classes/AirshipPlugin.m
+++ b/ios/Classes/AirshipPlugin.m
@@ -5,13 +5,4 @@ @implementation AirshipPlugin
+ (void)registerWithRegistrar:(NSObject*)registrar {
[SwiftAirshipPlugin registerWithRegistrar:registrar];
}
-
-+ (void)load {
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- [center addObserverForName:UIApplicationDidFinishLaunchingNotification
- object:nil
- queue:nil usingBlock:^(NSNotification * _Nonnull note) {
- [FlutterAirshipAutopilot.shared onLoadWithLaunchOptions:note.userInfo];
- }];
-}
@end
diff --git a/ios/Classes/AirshipPluginLoader.swift b/ios/Classes/AirshipPluginLoader.swift
new file mode 100644
index 00000000..d0b48993
--- /dev/null
+++ b/ios/Classes/AirshipPluginLoader.swift
@@ -0,0 +1,10 @@
+import AirshipFrameworkProxy
+
+@objc(AirshipPluginLoader)
+public class AirshipPluginLoader: NSObject, AirshipPluginLoaderProtocol {
+ public static func onApplicationDidFinishLaunching(
+ launchOptions: [UIApplication.LaunchOptionsKey : Any]?
+ ) {
+ AirshipAutopilot.shared.onLoad(launchOptions: launchOptions)
+ }
+}
diff --git a/ios/Classes/AirshipPluginVersion.swift b/ios/Classes/AirshipPluginVersion.swift
index 59d4c16c..6f9e4a3e 100644
--- a/ios/Classes/AirshipPluginVersion.swift
+++ b/ios/Classes/AirshipPluginVersion.swift
@@ -1,5 +1,5 @@
import Foundation
class AirshipPluginVersion {
- static let pluginVersion = "7.8.2"
+ static let pluginVersion = "7.9.0"
}
diff --git a/ios/Classes/SwiftAirshipPlugin.swift b/ios/Classes/SwiftAirshipPlugin.swift
index 1980f4c1..60c2899f 100644
--- a/ios/Classes/SwiftAirshipPlugin.swift
+++ b/ios/Classes/SwiftAirshipPlugin.swift
@@ -87,7 +87,7 @@ public class SwiftAirshipPlugin: NSObject, FlutterPlugin {
}
}
}
-
+
@MainActor
private func handle(_ call: FlutterMethodCall) async throws -> Any? {
switch call.method {
@@ -204,8 +204,18 @@ public class SwiftAirshipPlugin: NSObject, FlutterPlugin {
return nil
case "push#enableUserNotifications":
- return try await AirshipProxy.shared.push.enableUserPushNotifications()
-
+ guard let args = try? call.requireAnyArg() as? [String: Any] else {
+ throw AirshipErrors.error("Invalid argument type. Expected dictionary.")
+ }
+
+ let json = try AirshipJSON.wrap(args)
+
+ if let enableArgs: EnableUserPushNotificationsArgs = try? json.decode() {
+ return try? await AirshipProxy.shared.push.enableUserPushNotifications(args: enableArgs)
+ } else {
+ return try? await AirshipProxy.shared.push.enableUserPushNotifications()
+ }
+
case "push#isUserNotificationsEnabled":
return try AirshipProxy.shared.push.isUserNotificationsEnabled()
@@ -356,6 +366,19 @@ public class SwiftAirshipPlugin: NSObject, FlutterPlugin {
try call.requireBooleanArg()
)
return nil
+
+ case "messageCenter#showMessageCenter":
+ let optionalMessageId = call.arguments as? String
+ try AirshipProxy.shared.messageCenter.showMessageCenter(
+ messageID: optionalMessageId
+ )
+ return nil
+
+ case "messageCenter#showMessageView":
+ try AirshipProxy.shared.messageCenter.showMessageView(
+ messageID: try call.requireStringArg()
+ )
+ return nil
// Preference Center
case "preferenceCenter#display":
@@ -412,7 +435,6 @@ public class SwiftAirshipPlugin: NSObject, FlutterPlugin {
featuresNames: try call.requireStringArrayArg()
)
-
// Locale
case "locale#setLocaleOverride":
try AirshipProxy.shared.locale.setCurrentLocale(
@@ -444,7 +466,102 @@ public class SwiftAirshipPlugin: NSObject, FlutterPlugin {
) as? AirshipJSON
return result?.unWrap()
- // Feature Flag
+ // Live Activity
+
+ case "liveActivity#start":
+ guard let args = try? call.requireAnyArg() as? [String: Any] else {
+ throw AirshipErrors.error("Invalid argument type. Expected dictionary.")
+ }
+
+
+ if #available(iOS 16.1, *) {
+ do {
+
+ let json = try AirshipJSON.wrap(args)
+
+ let decoder = JSONDecoder()
+ decoder.dateDecodingStrategy = .iso8601
+ let start:LiveActivityRequest.Start = try json.decode(decoder: decoder)
+
+ let result = try AirshipJSON.wrap(try await LiveActivityManager.shared.start(start))
+
+ return result.unWrap()
+ } catch {
+ throw AirshipErrors.error("Unable to start request: \(error.localizedDescription)")
+ }
+ } else {
+ throw AirshipErrors.error("Not available before iOS 16.1")
+ }
+
+ case "liveActivity#update":
+ guard let args = try? call.requireAnyArg() as? [String: Any] else {
+ throw AirshipErrors.error("Invalid argument type. Expected dictionary.")
+ }
+
+
+ if #available(iOS 16.1, *) {
+ let json = try AirshipJSON.wrap(args)
+
+ let decoder = JSONDecoder()
+ decoder.dateDecodingStrategy = .iso8601
+ let update:LiveActivityRequest.Update = try json.decode(decoder: decoder)
+
+ try await LiveActivityManager.shared.update(update)
+
+ return nil
+ } else {
+ throw AirshipErrors.error("Not available before iOS 16.1")
+ }
+ case "liveActivity#listAll":
+ if #available(iOS 16.1, *) {
+ let result = try await LiveActivityManager.shared.listAll()
+ return try AirshipJSON.wrap(result).unWrap() as Any
+ } else {
+ throw AirshipErrors.error("Not available before 16.1")
+ }
+
+ case "liveActivity#list":
+ if #available(iOS 16.1, *) {
+ let result = try await LiveActivityManager.shared.list(try AirshipJSON.wrap(call.arguments).decode())
+ return try AirshipJSON.wrap(result).unWrap() as Any
+ } else {
+ throw AirshipErrors.error("Not available before 16.1")
+ }
+ case "liveActivity#stop":
+ guard let args = try? call.requireAnyArg() as? [String: Any] else {
+ throw AirshipErrors.error("Invalid argument type. Expected dictionary.")
+ }
+
+ if #available(iOS 16.1, *) {
+ let json = try AirshipJSON.wrap(args)
+
+ let decoder = JSONDecoder()
+ decoder.dateDecodingStrategy = .iso8601
+ let request:LiveActivityRequest.End = try json.decode(decoder: decoder)
+
+ try await LiveActivityManager.shared.end(request)
+
+ return nil
+ } else {
+ throw AirshipErrors.error("Not available before iOS 16.1")
+ }
+
+ case "liveUpdate#start":
+ throw AirshipErrors.error("Not available on iOS")
+
+ case "liveUpdate#listAll":
+ throw AirshipErrors.error("Not available on iOS")
+
+ case "liveUpdate#list":
+ throw AirshipErrors.error("Not available on iOS")
+
+ case "liveUpdate#update":
+ throw AirshipErrors.error("Not available on iOS")
+
+ case "liveUpdate#end":
+ throw AirshipErrors.error("Not available on iOS")
+
+ // Feature Flag
case "featureFlagManager#flag":
let flag = try await AirshipProxy.shared.featureFlagManager.flag(
name: try call.requireStringArg()
diff --git a/ios/airship_flutter.podspec b/ios/airship_flutter.podspec
index d0ffe797..c2913210 100644
--- a/ios/airship_flutter.podspec
+++ b/ios/airship_flutter.podspec
@@ -1,5 +1,5 @@
-AIRSHIP_FLUTTER_VERSION="7.8.2"
+AIRSHIP_FLUTTER_VERSION="7.9.0"
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
@@ -20,6 +20,6 @@ Airship flutter plugin.
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.ios.deployment_target = "14.0"
- s.dependency "AirshipFrameworkProxy", "7.3.0"
+ s.dependency "AirshipFrameworkProxy", "10.1.1"
end
diff --git a/lib/airship_flutter.dart b/lib/airship_flutter.dart
index 0a2eb0fb..4f28f0ee 100644
--- a/lib/airship_flutter.dart
+++ b/lib/airship_flutter.dart
@@ -20,7 +20,6 @@ export 'src/push_notification_status.dart';
export 'src/push_payload.dart';
export 'src/ios_push_options.dart';
-
export 'src/attribute_editor.dart';
export 'src/channel_scope.dart';
export 'src/custom_event.dart';
@@ -32,4 +31,7 @@ export 'src/scoped_subscription_list_editor.dart';
export 'src/subscription_list_editor.dart';
export 'src/tag_group_editor.dart';
export 'src/tag_editor.dart';
-
+export 'src/live_activity.dart';
+export 'src/live_update.dart';
+export 'src/airship_live_activity_manager.dart';
+export 'src/airship_live_update_manager.dart';
diff --git a/lib/src/airship_flutter.dart b/lib/src/airship_flutter.dart
index e50dd904..42f9304d 100644
--- a/lib/src/airship_flutter.dart
+++ b/lib/src/airship_flutter.dart
@@ -39,6 +39,12 @@ class Airship {
/// The [AirshipFeatureFlagManager] instance.
static final featureFlagManager = AirshipFeatureFlagManager(_module);
+ /// The [AirshipLiveActivityManager] instance.
+ static final liveActivityManager = AirshipLiveActivityManager(_module);
+
+ /// The [AirshipLiveUpdateManager] instance.
+ static final liveUpdateManager = AirshipLiveUpdateManager(_module);
+
//
/// Initializes Airship with the given config. Airship will store the config
/// and automatically use it during the next app init. Any chances to config
diff --git a/lib/src/airship_live_activity_manager.dart b/lib/src/airship_live_activity_manager.dart
new file mode 100644
index 00000000..d26dfc7c
--- /dev/null
+++ b/lib/src/airship_live_activity_manager.dart
@@ -0,0 +1,48 @@
+import 'airship_module.dart';
+import 'live_activity.dart';
+
+/// Live Activity manager.
+class AirshipLiveActivityManager {
+ final AirshipModule _module;
+
+ AirshipLiveActivityManager(AirshipModule module) : _module = module;
+
+ /// Starts a Live Activity.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future start(LiveActivityStartRequest request) async {
+ var response = await _module.channel
+ .invokeMethod('liveActivity#start', request.toJson());
+ return LiveActivity.fromJson(response);
+ }
+
+ /// Lists any Live Activities for the request.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future> list(LiveActivityListRequest request) async {
+ var response = await _module.channel
+ .invokeMethod('liveActivity#list', request.toJson());
+ return (response as List).map((e) => LiveActivity.fromJson(e)).toList();
+ }
+
+ /// Lists all Live Activities.
+ /// @returns A Future with the result.
+ Future> listAll() async {
+ var response = await _module.channel.invokeMethod('liveActivity#listAll');
+ return (response as List).map((e) => LiveActivity.fromJson(e)).toList();
+ }
+
+ /// Updates a Live Activity.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future update(LiveActivityUpdateRequest request) async {
+ await _module.channel.invokeMethod('liveActivity#update', request.toJson());
+ }
+
+ /// End a Live Activity.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future end(LiveActivityStopRequest request) async {
+ await _module.channel.invokeMethod('liveActivity#stop', request.toJson());
+ }
+}
diff --git a/lib/src/airship_live_update_manager.dart b/lib/src/airship_live_update_manager.dart
new file mode 100644
index 00000000..3497698a
--- /dev/null
+++ b/lib/src/airship_live_update_manager.dart
@@ -0,0 +1,64 @@
+import 'airship_module.dart';
+import 'live_update.dart';
+
+/// Live Update manager.
+class AirshipLiveUpdateManager {
+ final AirshipModule _module;
+
+ AirshipLiveUpdateManager(this._module);
+
+ /// Creates a Live Update.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future start(LiveUpdateStartRequest request) async {
+ await _module.channel.invokeMethod('liveUpdate#start', request.toJson());
+ }
+
+ /// Updates a Live Update.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future update(LiveUpdateUpdateRequest request) async {
+ await _module.channel.invokeMethod('liveUpdate#update', request.toJson());
+ }
+
+ /// Lists any Live Updates for the request.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future> list(LiveUpdateListRequest request) async {
+ var response =
+ await _module.channel.invokeMethod('liveUpdate#list', request.toJson());
+ return (response as List)
+ .map((e) => LiveUpdate.fromJson(e as Map))
+ .toList();
+ }
+
+ /// Lists all Live Updates.
+ /// @returns A Future with the result.
+ // Future> listAll() async {
+ // var response = await _module.channel.invokeMethod('liveUpdate#listAll');
+ // return (response as List)
+ // .map((e) => LiveUpdate.fromJson(e as Map))
+ // .toList();
+ // }
+
+ Future> listAll() async {
+ try {
+ final result = await _module.channel.invokeMethod('liveUpdate#listAll');
+ if (result is List) {
+ return result.map((item) => LiveUpdate.fromJson(item)).toList();
+ }
+ throw FormatException(
+ 'Invalid result format: expected a List, got ${result.runtimeType}');
+ } catch (e) {
+ print('Error listing all live updates: $e');
+ rethrow;
+ }
+ }
+
+ /// End a Live Update.
+ /// @param request The request options.
+ /// @returns A Future with the result.
+ Future end(LiveUpdateEndRequest request) async {
+ await _module.channel.invokeMethod('liveUpdate#end', request.toJson());
+ }
+}
diff --git a/lib/src/airship_message_center.dart b/lib/src/airship_message_center.dart
index 9586870a..6cafed13 100644
--- a/lib/src/airship_message_center.dart
+++ b/lib/src/airship_message_center.dart
@@ -3,72 +3,109 @@ import 'inbox_message.dart';
import 'airship_events.dart';
class AirshipMessageCenter {
-
final AirshipModule _module;
AirshipMessageCenter(AirshipModule module) : _module = module;
- /// Gets the current inbox messages.
+ /// Retrieves the current list of inbox messages.
+ ///
+ /// Returns a Future that resolves to a List of InboxMessage objects.
Future> get messages async {
- List inboxMessages = await (_module.channel.invokeMethod(
- "messageCenter#getMessages"));
+ List inboxMessages =
+ await (_module.channel.invokeMethod("messageCenter#getMessages"));
return inboxMessages.map((dynamic payload) {
return InboxMessage.fromJson(payload);
}).toList();
}
- /// Marks an inbox message with the [messageId] as read.
+ /// Marks a specific inbox message as read.
+ ///
+ /// [messageId] The unique identifier of the message to be marked as read.
+ /// Returns a Future that completes when the operation is finished.
Future markRead(String messageId) async {
- return await _module.channel.invokeMethod(
- 'messageCenter#markMessageRead', messageId);
+ return await _module.channel
+ .invokeMethod('messageCenter#markMessageRead', messageId);
}
- /// Requests to display the Message Center.
+ /// Requests to display the Message Center UI.
+ ///
+ /// [messageId] Optional. If provided, opens the Message Center to this specific message.
+ /// Returns a Future that completes when the display request is processed.
Future display([String? messageId]) async {
- return await _module.channel.invokeMethod(
- 'messageCenter#display', messageId);
+ return await _module.channel
+ .invokeMethod('messageCenter#display', messageId);
}
- /// Deletes an inbox message with the id [messageId].
+ /// Deletes a specific inbox message.
+ ///
+ /// [messageId] The unique identifier of the message to be deleted.
+ /// Returns a Future that completes when the deletion is finished.
Future deleteMessage(String messageId) async {
- return await _module.channel.invokeMethod(
- 'messageCenter#deleteMessage', messageId);
+ return await _module.channel
+ .invokeMethod('messageCenter#deleteMessage', messageId);
}
- /// Gets the unread count.
+ /// Retrieves the count of unread messages in the inbox.
+ ///
+ /// Returns a Future that resolves to an integer representing the unread count.
Future get unreadMessageCount async {
- return await _module.channel.invokeMethod(
- 'messageCenter#getUnreadMessageCount');
+ return await _module.channel
+ .invokeMethod('messageCenter#getUnreadMessageCount');
}
- /// Enables or disables showing the OOTB UI when requested to display.
+ /// Configures whether the default Message Center UI should automatically launch when requested.
+ ///
+ /// [enabled] If true, enables auto-launch; if false, disables it.
+ /// Returns a Future that completes when the setting is applied.
Future setAutoLaunchDefaultMessageCenter(bool enabled) async {
- return await _module.channel.invokeMethod(
- 'messageCenter#setAutoLaunch', enabled);
+ return await _module.channel
+ .invokeMethod('messageCenter#setAutoLaunch', enabled);
}
- /// Forces the inbox to refresh.
+ /// Displays the Message Center UI, overriding the auto-launch setting.
///
- /// This is normally not needed as the inbox will automatically refresh on
- /// foreground or when a push arrives that's associated with a message.
+ /// This method will show the Message Center regardless of the auto-launch configuration.
+ /// [messageId] Optional. If provided, opens the Message Center to this specific message.
+ /// Returns a Future that completes when the Message Center is displayed.
+ Future showMessageCenter([String? messageId]) {
+ return _module.channel
+ .invokeMethod('messageCenter#showMessageCenter', messageId);
+ }
+
+ /// Displays a specific message view, overriding the auto-launch setting.
+ ///
+ /// This method will show the message view for a specific message, regardless of the auto-launch configuration.
+ /// [messageId] The unique identifier of the message to display.
+ /// Returns a Future that completes when the message view is displayed.
+ Future showMessageView(String messageId) {
+ return _module.channel
+ .invokeMethod('messageCenter#showMessageView', messageId);
+ }
+
+ /// Forces a refresh of the inbox messages.
+ ///
+ /// This method is typically not needed as the inbox automatically refreshes on app foreground or when a new message arrives.
+ /// Returns a Future that resolves to a boolean indicating the success of the refresh operation.
Future refreshInbox() async {
return _module.channel.invokeMethod("messageCenter#refreshMessages");
}
- /// Gets inbox updated event stream.
+ /// Provides a stream of events for when the inbox is updated.
+ ///
+ /// Returns a Stream of MessageCenterUpdatedEvent objects.
Stream get onInboxUpdated {
return _module
.getEventStream("com.airship.flutter/event/message_center_updated")
.map((dynamic value) => MessageCenterUpdatedEvent.fromJson(value));
-
}
- /// Gets show inbox event stream. Events will only be
- /// emitted if [setAutoLaunchDefaultMessageCenter] is disabled.
+ /// Provides a stream of events for when the Message Center should be displayed.
+ ///
+ /// This stream will only emit events if auto-launch is disabled via setAutoLaunchDefaultMessageCenter.
+ /// Returns a Stream of DisplayMessageCenterEvent objects.
Stream get onDisplay {
return _module
.getEventStream("com.airship.flutter/event/display_message_center")
.map((dynamic value) => DisplayMessageCenterEvent.fromJson(value));
}
}
-
diff --git a/lib/src/airship_push.dart b/lib/src/airship_push.dart
index d6c44798..3b70d0c0 100644
--- a/lib/src/airship_push.dart
+++ b/lib/src/airship_push.dart
@@ -10,7 +10,6 @@ import 'package:flutter/widgets.dart' hide Notification;
import 'airship_utils.dart';
class AirshipPush {
-
final AirshipModule _module;
final IOSPush iOS;
final AndroidPush android;
@@ -22,25 +21,32 @@ class AirshipPush {
/// Tells if user notifications are enabled or not.
Future get isUserNotificationsEnabled async {
- return await _module.channel.invokeMethod(
- 'push#isUserNotificationsEnabled');
+ return await _module.channel
+ .invokeMethod('push#isUserNotificationsEnabled');
}
/// Enables or disables the user notifications.
Future setUserNotificationsEnabled(bool enabled) async {
- return await _module.channel.invokeMethod(
- 'push#setUserNotificationsEnabled', enabled);
+ return await _module.channel
+ .invokeMethod('push#setUserNotificationsEnabled', enabled);
}
/// Enables user notifications.
- Future enableUserNotifications() async {
- return await _module.channel.invokeMethod('push#enableUserNotifications');
+ ///
+ /// [args] Optional arguments for enabling user notifications.
+ ///
+ /// Returns a Future with the permission result. The result may be null if the operation fails.
+ Future enableUserNotifications(
+ {EnableUserPushNotificationsArgs? options}) async {
+ final Map arguments = options?.toJson() ?? {};
+ return await _module.channel
+ .invokeMethod('push#enableUserNotifications', arguments);
}
/// Gets the notification status.
Future get notificationStatus async {
- var payload = await _module.channel.invokeMethod(
- 'push#getNotificationStatus');
+ var payload =
+ await _module.channel.invokeMethod('push#getNotificationStatus');
return PushNotificationStatus.fromJson(Map.from(payload));
}
@@ -54,7 +60,7 @@ class AirshipPush {
/// Supported on Android Marshmallow (23)+ and iOS 10+.
Future> get activeNotifications async {
List notifications =
- await (_module.channel.invokeMethod('push#getActiveNotifications'));
+ await (_module.channel.invokeMethod('push#getActiveNotifications'));
return notifications.map((dynamic payload) {
return PushPayload.fromJson(payload);
}).toList();
@@ -64,8 +70,8 @@ class AirshipPush {
///
/// The [notification] parameter is the notification ID.
Future clearNotification(String notification) async {
- return await _module.channel.invokeMethod(
- 'push#clearNotification', notification);
+ return await _module.channel
+ .invokeMethod('push#clearNotification', notification);
}
/// Clears all notifications for the application.
@@ -99,7 +105,7 @@ class AirshipPush {
return _module
.getEventStream("com.airship.flutter/event/notification_status_changed")
.map((dynamic value) =>
- PushNotificationStatusChangedEvent.fromJson(value));
+ PushNotificationStatusChangedEvent.fromJson(value));
}
}
@@ -108,8 +114,7 @@ class AndroidPush {
final AirshipModule _module;
static bool _isBackgroundHandlerSet = false;
- AndroidPush(AirshipModule module)
- : _module = module;
+ AndroidPush(AirshipModule module) : _module = module;
/// Sets a background message handler.
Future setBackgroundPushReceivedHandler(
@@ -123,8 +128,7 @@ class AndroidPush {
}
_isBackgroundHandlerSet = true;
- final isolateCallback =
- PluginUtilities.getCallbackHandle(
+ final isolateCallback = PluginUtilities.getCallbackHandle(
_androidBackgroundMessageIsolateCallback)!;
final messageCallback = PluginUtilities.getCallbackHandle(handler)!;
await _module.channel.invokeMapMethod("startBackgroundIsolate", {
@@ -134,7 +138,6 @@ class AndroidPush {
}
}
-
@pragma('vm:entry-point')
void _androidBackgroundMessageIsolateCallback() {
WidgetsFlutterBinding.ensureInitialized();
@@ -144,7 +147,7 @@ void _androidBackgroundMessageIsolateCallback() {
final args = call.arguments;
final handle = CallbackHandle.fromRawHandle(args["messageCallback"]);
final callback = PluginUtilities.getCallbackFromHandle(handle)
- as AndroidBackgroundPushReceivedHandler;
+ as AndroidBackgroundPushReceivedHandler;
try {
final event = PushReceivedEvent.fromJson(args["event"]);
await callback(event);
@@ -158,23 +161,22 @@ void _androidBackgroundMessageIsolateCallback() {
});
// Tell the native side to start the background isolate.
- AirshipModule.singleton.backgroundChannel.invokeMethod(
- "backgroundIsolateStarted");
+ AirshipModule.singleton.backgroundChannel
+ .invokeMethod("backgroundIsolateStarted");
}
/// Specific iOS Push configuration
class IOSPush {
final AirshipModule _module;
- IOSPush(AirshipModule module)
- : _module = module;
+ IOSPush(AirshipModule module) : _module = module;
/// Checks if auto-badging is enabled on iOS. Badging is not supported for Android.
Future isAutoBadgeEnabled() async {
var isAutoBadgeEnabled = false;
if (Platform.isIOS) {
isAutoBadgeEnabled =
- await _module.channel.invokeMethod('push#ios#isAutobadgeEnabled');
+ await _module.channel.invokeMethod('push#ios#isAutobadgeEnabled');
}
return isAutoBadgeEnabled;
}
@@ -213,8 +215,8 @@ class IOSPush {
}
}
- return await _module.channel.invokeMethod(
- 'push#ios#setNotificationOptions', strings);
+ return await _module.channel
+ .invokeMethod('push#ios#setNotificationOptions', strings);
}
/// Sets the notification options.
@@ -242,8 +244,8 @@ class IOSPush {
}
}
- return await _module.channel.invokeMethod(
- 'push#ios#setForegroundPresentationOptions', strings);
+ return await _module.channel
+ .invokeMethod('push#ios#setForegroundPresentationOptions', strings);
}
/// Enables or disables auto-badging on iOS. Badging is not supported for Android.
@@ -252,8 +254,8 @@ class IOSPush {
return Future.value();
}
- return await _module.channel.invokeMethod(
- 'push#ios#setAutobadgeEnabled', enabled);
+ return await _module.channel
+ .invokeMethod('push#ios#setAutobadgeEnabled', enabled);
}
/// Sets the [badge] number on iOS. Badging is not supported for Android.
@@ -261,8 +263,7 @@ class IOSPush {
if (!Platform.isIOS) {
return Future.value();
}
- return await _module.channel.invokeMethod(
- 'push#ios#setBadgeNumber', badge);
+ return await _module.channel.invokeMethod('push#ios#setBadgeNumber', badge);
}
/// Gets the [badge] number on iOS. Badging is not supported for Android.
@@ -282,46 +283,43 @@ class IOSPush {
}
/// Gets the authorized notification settings.
- Future> get authorizedNotificationSettings async {
+ Future>
+ get authorizedNotificationSettings async {
if (!Platform.isIOS) {
return Future.value(List.empty());
}
- var strings = List.from(
- await _module.channel.invokeMethod(
- 'push#ios#getAuthorizedNotificationSettings')
- );
+ var strings = List.from(await _module.channel
+ .invokeMethod('push#ios#getAuthorizedNotificationSettings'));
return AirshipUtils.parseIOSAuthorizedSettings(strings);
}
/// Gets the authorized notification status.
- Future<
- IOSAuthorizedNotificationStatus> get authorizedNotificationStatus async {
+ Future
+ get authorizedNotificationStatus async {
if (!Platform.isIOS) {
return Future.value(IOSAuthorizedNotificationStatus.notDetermined);
}
- var status = await _module.channel.invokeMethod(
- 'push#ios#getAuthorizedNotificationStatus');
+ var status = await _module.channel
+ .invokeMethod('push#ios#getAuthorizedNotificationStatus');
return AirshipUtils.parseIOSAuthorizedStatus(status);
}
/// Gets the authorized settings changed event stream.
- Stream<
- IOSAuthorizedNotificationSettingsChangedEvent> get onAuthorizedSettingsChanged {
-
+ Stream
+ get onAuthorizedSettingsChanged {
if (!Platform.isIOS) {
return Stream.empty();
}
return _module
.getEventStream(
- "com.airship.flutter/event/ios_authorized_notification_settings_changed")
+ "com.airship.flutter/event/ios_authorized_notification_settings_changed")
.map((dynamic value) =>
- IOSAuthorizedNotificationSettingsChangedEvent.fromJson(value));
+ IOSAuthorizedNotificationSettingsChangedEvent.fromJson(value));
}
}
-typedef AndroidBackgroundPushReceivedHandler = Future<
- void> Function(PushReceivedEvent pushReceivedEvent);
+typedef AndroidBackgroundPushReceivedHandler = Future Function(
+ PushReceivedEvent pushReceivedEvent);
diff --git a/lib/src/live_activity.dart b/lib/src/live_activity.dart
new file mode 100644
index 00000000..f011f5f9
--- /dev/null
+++ b/lib/src/live_activity.dart
@@ -0,0 +1,187 @@
+/// Live Activity info.
+class LiveActivity {
+ final String id;
+ final String attributeTypes;
+ final LiveActivityContent content;
+ final Map attributes;
+ final String state;
+
+ LiveActivity({
+ required this.id,
+ required this.attributeTypes,
+ required this.content,
+ required this.attributes,
+ required this.state,
+ });
+
+ static Map _ensureStringDynamicMap(dynamic data) {
+ if (data is Map) {
+ return data;
+ } else if (data is Map) {
+ return data.map((key, value) => MapEntry(key.toString(),
+ value is Map ? _ensureStringDynamicMap(value) : value));
+ }
+ throw FormatException(
+ 'Invalid data format: expected a Map, got ${data.runtimeType}');
+ }
+
+ factory LiveActivity.fromJson(dynamic json) {
+ final Map data = _ensureStringDynamicMap(json);
+
+ return LiveActivity(
+ id: data['id'] as String? ?? '',
+ attributeTypes: data['attributeTypes'] as String? ?? '',
+ content: LiveActivityContent.fromJson(data['content']),
+ attributes: _ensureStringDynamicMap(data['attributes'] ?? {}),
+ state: data['state'] as String? ?? '',
+ );
+ }
+
+ Map toJson() => {
+ 'id': id,
+ 'attributeTypes': attributeTypes,
+ 'content': content.toJson(),
+ 'attributes': attributes,
+ 'state': state,
+ };
+}
+
+class LiveActivityContent {
+ final Map state;
+ final String? staleDate;
+ final double relevanceScore;
+
+ LiveActivityContent({
+ required this.state,
+ this.staleDate,
+ required this.relevanceScore,
+ });
+
+ factory LiveActivityContent.fromJson(dynamic json) {
+ final Map data =
+ LiveActivity._ensureStringDynamicMap(json);
+
+ return LiveActivityContent(
+ state: LiveActivity._ensureStringDynamicMap(data['state'] ?? {}),
+ staleDate: data['staleDate'] as String?,
+ relevanceScore: (data['relevanceScore'] as num?)?.toDouble() ?? 0.0,
+ );
+ }
+
+ Map toJson() => {
+ 'state': state,
+ 'staleDate': staleDate,
+ 'relevanceScore': relevanceScore,
+ };
+}
+
+/// Base Live Activity request.
+abstract class LiveActivityRequest {
+ final String attributesType;
+
+ const LiveActivityRequest({required this.attributesType});
+
+ Map toJson();
+}
+
+/// Live Activity list request.
+class LiveActivityListRequest extends LiveActivityRequest {
+ const LiveActivityListRequest({required String attributesType})
+ : super(attributesType: attributesType);
+
+ @override
+ Map toJson() => {'attributesType': attributesType};
+}
+
+/// Live Activity start request.
+class LiveActivityStartRequest extends LiveActivityRequest {
+ final LiveActivityContent content;
+ final Map attributes;
+
+ const LiveActivityStartRequest({
+ required String attributesType,
+ required this.content,
+ required this.attributes,
+ }) : super(attributesType: attributesType);
+
+ @override
+ Map toJson() => {
+ 'attributesType': attributesType,
+ 'content': content.toJson(),
+ 'attributes': attributes,
+ };
+}
+
+/// Live Activity update request.
+class LiveActivityUpdateRequest extends LiveActivityRequest {
+ final String activityId;
+ final LiveActivityContent content;
+
+ const LiveActivityUpdateRequest({
+ required String attributesType,
+ required this.activityId,
+ required this.content,
+ }) : super(attributesType: attributesType);
+
+ @override
+ Map toJson() => {
+ 'attributesType': attributesType,
+ 'activityId': activityId,
+ 'content': content.toJson(),
+ };
+}
+
+/// Live Activity end request.
+class LiveActivityStopRequest extends LiveActivityRequest {
+ final String activityId;
+ final LiveActivityContent? content;
+ final LiveActivityDismissalPolicy dismissalPolicy;
+
+ const LiveActivityStopRequest({
+ required String attributesType,
+ required this.activityId,
+ this.content,
+ this.dismissalPolicy = const LiveActivityDismissalPolicyDefault(),
+ }) : super(attributesType: attributesType);
+
+ @override
+ Map toJson() => {
+ 'attributesType': attributesType,
+ 'activityId': activityId,
+ if (content != null) 'content': content!.toJson(),
+ 'dismissalPolicy': dismissalPolicy.toJson(),
+ };
+}
+
+/// Live Activity dismissal policy.
+abstract class LiveActivityDismissalPolicy {
+ const LiveActivityDismissalPolicy();
+
+ Map toJson();
+}
+
+class LiveActivityDismissalPolicyImmediate extends LiveActivityDismissalPolicy {
+ const LiveActivityDismissalPolicyImmediate();
+
+ @override
+ Map toJson() => const {'type': 'immediate'};
+}
+
+class LiveActivityDismissalPolicyDefault extends LiveActivityDismissalPolicy {
+ const LiveActivityDismissalPolicyDefault();
+
+ @override
+ Map toJson() => const {'type': 'default'};
+}
+
+class LiveActivityDismissalPolicyAfterDate extends LiveActivityDismissalPolicy {
+ final String date;
+
+ const LiveActivityDismissalPolicyAfterDate(this.date);
+
+ @override
+ Map toJson() => {
+ 'type': 'after',
+ 'date': date,
+ };
+}
diff --git a/lib/src/live_update.dart b/lib/src/live_update.dart
new file mode 100644
index 00000000..cd168134
--- /dev/null
+++ b/lib/src/live_update.dart
@@ -0,0 +1,118 @@
+/// Live Update info.
+class LiveUpdate {
+ final String name;
+ final String type;
+ final dynamic content;
+ final String lastContentUpdateTimestamp;
+ final String lastStateChangeTimestamp;
+ final String? dismissTimestamp;
+
+ LiveUpdate({
+ required this.name,
+ required this.type,
+ required this.content,
+ required this.lastContentUpdateTimestamp,
+ required this.lastStateChangeTimestamp,
+ this.dismissTimestamp,
+ });
+
+ factory LiveUpdate.fromJson(dynamic json) {
+ return LiveUpdate(
+ name: json['name'] as String,
+ type: json['type'] as String,
+ content: json['content'] as dynamic,
+ lastContentUpdateTimestamp: json['lastContentUpdateTimestamp'] as String,
+ lastStateChangeTimestamp: json['lastStateChangeTimestamp'] as String,
+ dismissTimestamp: json['dismissTimestamp'] as String?,
+ );
+ }
+
+ Map toJson() => {
+ 'name': name,
+ 'type': type,
+ 'content': content,
+ 'lastContentUpdateTimestamp': lastContentUpdateTimestamp,
+ 'lastStateChangeTimestamp': lastStateChangeTimestamp,
+ 'dismissTimestamp': dismissTimestamp,
+ };
+}
+
+/// Live Update list request.
+class LiveUpdateListRequest {
+ final String type;
+
+ const LiveUpdateListRequest({required this.type});
+
+ Map toJson() => {'type': type};
+}
+
+/// Live Update update request.
+class LiveUpdateUpdateRequest {
+ final String name;
+ final Map content;
+ final String? timestamp;
+ final String? dismissTimestamp;
+
+ const LiveUpdateUpdateRequest({
+ required this.name,
+ required this.content,
+ this.timestamp,
+ this.dismissTimestamp,
+ });
+
+ Map toJson() => {
+ 'name': name,
+ 'content': content,
+ if (timestamp != null) 'timestamp': timestamp,
+ if (dismissTimestamp != null) 'dismissTimestamp': dismissTimestamp,
+ };
+}
+
+/// Live Update end request.
+class LiveUpdateEndRequest {
+ final String name;
+ final Map? content;
+ final String? timestamp;
+ final String? dismissTimestamp;
+
+ const LiveUpdateEndRequest({
+ required this.name,
+ this.content,
+ this.timestamp,
+ this.dismissTimestamp,
+ });
+
+ Map toJson() => {
+ 'name': name,
+ if (content != null) 'content': content,
+ if (timestamp != null) 'timestamp': timestamp,
+ if (dismissTimestamp != null) 'dismissTimestamp': dismissTimestamp,
+ };
+}
+
+/// Live Update start request.
+class LiveUpdateStartRequest {
+ final String name;
+ final String type;
+ final Map content;
+ final String? timestamp;
+ final String? dismissalTimestamp;
+
+ const LiveUpdateStartRequest({
+ required this.name,
+ required this.type,
+ required this.content,
+ this.timestamp,
+ this.dismissalTimestamp,
+ });
+
+ Map toJson() {
+ return {
+ 'name': name,
+ 'type': type,
+ 'content': content,
+ if (timestamp != null) 'timestamp': timestamp,
+ if (dismissalTimestamp != null) 'dismissalTimestamp': dismissalTimestamp,
+ };
+ }
+}
diff --git a/lib/src/push_notification_status.dart b/lib/src/push_notification_status.dart
index 0c9e224a..92bd2299 100644
--- a/lib/src/push_notification_status.dart
+++ b/lib/src/push_notification_status.dart
@@ -1,7 +1,5 @@
-
/// Push notification status object.
class PushNotificationStatus {
-
/// If user notifications are enabled.
final bool isUserNotificationsEnabled;
@@ -21,26 +19,85 @@ class PushNotificationStatus {
/// is true but `isOptedIn` is false, that means push token was not able to be registered.
final bool isUserOptedIn;
+ /// The notification permission status.
+ final PermissionStatus notificationPermissionStatus;
+
const PushNotificationStatus._internal(
- this.isUserNotificationsEnabled, this.areNotificationsAllowed, this.isPushPrivacyFeatureEnabled,
- this.isPushTokenRegistered, this.isOptedIn, this.isUserOptedIn);
+ this.isUserNotificationsEnabled,
+ this.areNotificationsAllowed,
+ this.isPushPrivacyFeatureEnabled,
+ this.isPushTokenRegistered,
+ this.isOptedIn,
+ this.isUserOptedIn,
+ this.notificationPermissionStatus);
static PushNotificationStatus fromJson(dynamic json) {
- var isUserNotificationsEnabled = json["isUserNotificationsEnabled"] ?? false;
+ var isUserNotificationsEnabled =
+ json["isUserNotificationsEnabled"] ?? false;
var areNotificationsAllowed = json["areNotificationsAllowed"] ?? false;
- var isPushPrivacyFeatureEnabled = json["isPushPrivacyFeatureEnabled"] ?? false;
+ var isPushPrivacyFeatureEnabled =
+ json["isPushPrivacyFeatureEnabled"] ?? false;
var isPushTokenRegistered = json["isPushTokenRegistered"] ?? false;
var isOptedIn = json["isOptedIn"] ?? false;
var isUserOptedIn = json["isUserOptedIn"] ?? false;
+ var notificationPermissionStatus =
+ json["notificationPermissionStatus"] ?? PermissionStatus.notDetermined;
return PushNotificationStatus._internal(
- isUserNotificationsEnabled, areNotificationsAllowed, isPushPrivacyFeatureEnabled,
- isPushTokenRegistered, isOptedIn, isUserOptedIn);
+ isUserNotificationsEnabled,
+ areNotificationsAllowed,
+ isPushPrivacyFeatureEnabled,
+ isPushTokenRegistered,
+ isOptedIn,
+ isUserOptedIn,
+ notificationPermissionStatus);
}
@override
String toString() {
return "PushNotificationStatus(isUserNotificationsEnabled=$isUserNotificationsEnabled,"
" areNotificationsAllowed=$areNotificationsAllowed, isPushPrivacyFeatureEnabled=$isPushPrivacyFeatureEnabled,"
- " isPushTokenRegistered=$isPushTokenRegistered, isOptedIn=$isOptedIn, isUserOptedIn=$isUserOptedIn)";
+ " isPushTokenRegistered=$isPushTokenRegistered, isOptedIn=$isOptedIn, isUserOptedIn=$isUserOptedIn, notificationPermissionStatus=$notificationPermissionStatus)";
+ }
+}
+
+/// Enum of permission status.
+enum PermissionStatus {
+ /// Permission is granted.
+ granted,
+
+ /// Permission is denied.
+ denied,
+
+ /// Permission has not yet been requested.
+ notDetermined,
+}
+
+/// Fallback when prompting for permission and the permission is
+/// already denied on iOS or is denied silently on Android.
+enum PromptPermissionFallback {
+ // Take the user to the system settings to enable the permission.
+ systemSettings,
+}
+
+/// Options for enabling push notifications.
+class EnableUserPushNotificationsArgs {
+ /// Optional fallback strategy.
+ final PromptPermissionFallback? fallback;
+
+ /// Creates a new instance of [EnableUserPushNotificationsArgs].
+ ///
+ /// Both [fallback] and [options] are optional.
+ EnableUserPushNotificationsArgs({
+ this.fallback,
+ });
+
+ Map toJson() {
+ final Map json = {};
+
+ if (fallback != null) {
+ json['fallback'] = fallback!.name;
+ }
+
+ return json;
}
-}
\ No newline at end of file
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 629521c7..218cc609 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: airship_flutter
description: "Cross-platform plugin interface for the native Airship iOS and Android SDKs. Simplifies adding Airship to Flutter apps."
-version: 7.8.2
+version: 7.9.0
homepage: https://www.airship.com/
repository: https://github.com/urbanairship/airship-flutter
issue_tracker: https://github.com/urbanairship/airship-flutter/issues