Skip to content

Commit

Permalink
Merge pull request #220 from urbanairship/MOBILE-4726
Browse files Browse the repository at this point in the history
Add LiveActivities and LiveUpdate support, push and message center updates
  • Loading branch information
crow authored Oct 21, 2024
2 parents 2732ffd + aa2f8d4 commit 37e1559
Show file tree
Hide file tree
Showing 61 changed files with 2,166 additions and 327 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
3 changes: 2 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ include: package:lints/recommended.yaml

linter:
rules:
constant_identifier_names: false
constant_identifier_names: false
prefer_const_constructors: false
14 changes: 5 additions & 9 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

group 'com.airship.airship'
version '1.0-SNAPSHOT'

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 {
Expand Down Expand Up @@ -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 {
Expand Down
31 changes: 31 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,36 @@
<application>
<meta-data android:name="com.urbanairship.autopilot"
android:value="com.airship.flutter.FlutterAutopilot"/>

<activity
android:name="com.urbanairship.android.framework.proxy.CustomMessageCenterActivity"
android:label="@string/ua_message_center_title"
android:theme="@style/Theme.AppCompat.DayNight"
android:launchMode="singleTask"
android:exported="false">

<intent-filter>
<action android:name="com.urbanairship.VIEW_RICH_PUSH_INBOX" />
<data android:scheme="message" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<intent-filter>
<action android:name="com.urbanairship.VIEW_RICH_PUSH_INBOX" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<activity
android:name="com.urbanairship.android.framework.proxy.CustomMessageActivity"
android:theme="@style/Theme.AppCompat.DayNight"
android:exported="false">
<intent-filter>
<action android:name="com.urbanairship.VIEW_RICH_PUSH_MESSAGE" />
<data android:scheme="message" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

</application>
</manifest>
153 changes: 150 additions & 3 deletions android/src/main/kotlin/com/airship/flutter/AirshipPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {

Expand Down Expand Up @@ -109,13 +114,15 @@ 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
"startBackgroundIsolate" -> startBackgroundIsolate(call, result)

// Airship
"takeOff" -> result.resolveResult(call) { proxy.takeOff(call.jsonArgs()) }

"isFlying" -> result.resolveResult(call) { proxy.isFlying() }

// Channel
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()) }
Expand Down Expand Up @@ -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 ->
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -66,7 +64,6 @@ class FlutterAutopilot : BaseAutopilot() {
}
}


companion object {
private val APP_KEY = stringPreferencesKey("app_key")
private val APP_SECRET = stringPreferencesKey("app_secret")
Expand Down
Loading

0 comments on commit 37e1559

Please sign in to comment.