Skip to content

Commit

Permalink
Merge pull request #236 from CleverTap/develop
Browse files Browse the repository at this point in the history
Release v2.4.0
  • Loading branch information
Anush-Shand authored May 10, 2024
2 parents e45f27b + 0272ad6 commit 96922e4
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 90 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
## CHANGE LOG

### Version 2.4.0 *(10th May 2024)*
-------------------------------------------
**What's new**
* **[Web Platform]**
* Added the method 'recordChargedEvent' for web

**Bug Fixes**
* **[Web Platform]**
* Added [JS package](https://pub.dev/packages/js) dependency to handle latest versions.

* **[Android Platform]**
* Fixes [#114](https://github.com/CleverTap/clevertap-flutter/issues/114) - an issue related to callbacks from native to dart when a 3rd party plugin like `flutter_workmanager` is used.

### Version 2.3.1 *(19th April 2024)*
-------------------------------------------
**Bug Fixes**
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ To get started, sign up [here](https://clevertap.com/live-product-demo/).

```yaml
dependencies:
clevertap_plugin: 2.3.1
clevertap_plugin: 2.4.0
```
- Run `flutter packages get` to install the SDK
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group 'com.clevertap.clevertap_plugin'
version '2.3.1'
version '2.4.0'

rootProject.allprojects {
repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
import com.clevertap.clevertap_plugin.CleverTapTypeUtils.LongUtil;
import com.clevertap.clevertap_plugin.isolate.IsolateHandlePreferences;

import java.util.HashSet;
import java.util.Set;
import org.json.JSONException;
import org.json.JSONObject;

Expand Down Expand Up @@ -93,13 +95,14 @@ public class CleverTapPlugin implements ActivityAware,

private MethodChannel dartToNativeMethodChannel;

private static MethodChannel nativeToDartMethodChannel;
private MethodChannel lastNativeToDartMethodChannel;

private CleverTapAPI cleverTapAPI;

private Context context;

public static Map<String, Object> variables = new HashMap<>();
private static final Set<MethodChannel> nativeToDartMethodChannelSet = new HashSet<>();

/**
* Plugin registration.
Expand Down Expand Up @@ -153,6 +156,7 @@ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
Log.d(TAG,"onAttachedToEngine");
setupPlugin(binding.getApplicationContext(), binding.getBinaryMessenger(), null);
}

Expand All @@ -168,8 +172,10 @@ public void onDetachedFromActivityForConfigChanges() {

@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
Log.d(TAG,"onDetachedFromEngine");
nativeToDartMethodChannelSet.remove(this.lastNativeToDartMethodChannel);
this.lastNativeToDartMethodChannel = null;
dartToNativeMethodChannel = null;
nativeToDartMethodChannel = null;
context = null;
}

Expand Down Expand Up @@ -1395,49 +1401,55 @@ private void initializeInbox(Result result) {
}

private void invokeMethodOnUiThread(final String methodName, final String cleverTapID) {
final MethodChannel channel = nativeToDartMethodChannel;
if (channel == null) {
Log.d(TAG, "methodChannel in invokeMethodOnUiThread(String) is null");
return;
}
runOnMainThread(() -> {
if (!cleverTapID.isEmpty()) {
channel.invokeMethod(methodName, cleverTapID);
} else {
channel.invokeMethod(methodName, null);
Log.d(TAG, "methodChannelSet in invokeMethodOnUiThread(String) is of size " + nativeToDartMethodChannelSet.size());

for(MethodChannel channel : nativeToDartMethodChannelSet) {
if (channel != null) {
Log.d(TAG, "methodChannelSet in invokeMethodOnUiThread(String) " + channel);
runOnMainThread(() -> {
if (!cleverTapID.isEmpty()) {
channel.invokeMethod(methodName, cleverTapID);
} else {
channel.invokeMethod(methodName, null);
}
});
}
});
}
}

@SuppressWarnings("SameParameterValue")
private void invokeMethodOnUiThread(final String methodName, final boolean params) {
final MethodChannel channel = nativeToDartMethodChannel;
if (channel == null) {
Log.d(TAG, "params in invokeMethodOnUiThread(boolean) is null");
return;
Log.d(TAG, "methodChannelSet in invokeMethodOnUiThread(boolean) is of size" + nativeToDartMethodChannelSet.size());

for(MethodChannel channel : nativeToDartMethodChannelSet) {
if (channel != null) {
Log.d(TAG, "methodChannelSet in invokeMethodOnUiThread(boolean) " + channel);
runOnMainThread(() -> channel.invokeMethod(methodName, params));
}
}
runOnMainThread(() -> {
channel.invokeMethod(methodName, params);
});
}

private void invokeMethodOnUiThread(final String methodName, final Map map) {
final MethodChannel channel = nativeToDartMethodChannel;
if (channel == null) {
Log.d(TAG, "methodChannel in invokeMethodOnUiThread(Map) is null");
return;
Log.d(TAG, "methodChannelSet in invokeMethodOnUiThread(Map) is of size " + nativeToDartMethodChannelSet.size());

for(MethodChannel channel : nativeToDartMethodChannelSet) {
if (channel != null) {
Log.d(TAG, "methodChannel in invokeMethodOnUiThread(Map) " + channel);
runOnMainThread(() -> channel.invokeMethod(methodName, map));
}
}
runOnMainThread(() -> channel.invokeMethod(methodName, map));
}

@SuppressWarnings("SameParameterValue")
private void invokeMethodOnUiThread(final String methodName, final ArrayList list) {
final MethodChannel channel = nativeToDartMethodChannel;
if (channel == null) {
Log.d(TAG, "methodChannel in invokeMethodOnUiThread(ArrayList) is null");
return;
Log.d(TAG, "methodChannelSet in invokeMethodOnUiThread(ArrayList) is of size " + nativeToDartMethodChannelSet.size());

for(MethodChannel channel : nativeToDartMethodChannelSet) {
if (channel != null) {
Log.d(TAG, "methodChannel in invokeMethodOnUiThread(ArrayList)" + channel);
runOnMainThread(() -> channel.invokeMethod(methodName, list));
}
}
runOnMainThread(() -> channel.invokeMethod(methodName, list));
}

private boolean isCleverTapNotNull(CleverTapAPI cleverTapAPI) {
Expand Down Expand Up @@ -1880,13 +1892,12 @@ private MethodChannel getMethodChannel(String channelName, BinaryMessenger messe

private void setupPlugin(Context context, BinaryMessenger messenger, Registrar registrar) {
this.dartToNativeMethodChannel = getMethodChannel("clevertap_plugin/dart_to_native", messenger, registrar);
if (nativeToDartMethodChannel == null) {
// set nativeToDartMethodChannel channel once and it has to be static field
// as per https://github.com/firebase/flutterfire/issues/9689 because multiple
// instances of the CleverTap plugin can be created in case onBackgroundMessage handler
// of FCM plugin.
nativeToDartMethodChannel = getMethodChannel("clevertap_plugin/native_to_dart", messenger, registrar);
}

// lastNativeToDartMethodChannel is added to a set and not kept as a static field to ensure callbacks work when a background isolate is spawned
// Background Isolates are spawned by several libraries like flutter_workmanager and flutter_firebasemessaging
lastNativeToDartMethodChannel = getMethodChannel("clevertap_plugin/native_to_dart", messenger, registrar);
nativeToDartMethodChannelSet.add(lastNativeToDartMethodChannel);

this.dartToNativeMethodChannel.setMethodCallHandler(this);
this.context = context.getApplicationContext();
this.cleverTapAPI = CleverTapAPI.getDefaultInstance(this.context);
Expand Down
10 changes: 5 additions & 5 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ dependencies {
//implementation fileTree('libs')
implementation "com.clevertap.android:push-templates:1.2.3"
implementation 'com.google.firebase:firebase-messaging:23.4.1'
implementation 'androidx.core:core:1.3.0'
implementation 'androidx.fragment:fragment:1.3.6'
implementation 'androidx.core:core:1.13.1'
implementation 'androidx.fragment:fragment:1.7.0'
//MANDATORY for App Inbox
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.viewpager:viewpager:1.0.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'

//Optional ExoPlayer Libraries for Audio/Video Inbox Messages. Audio/Video messages will be dropped without these dependencies
Expand Down
51 changes: 45 additions & 6 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,64 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io' show Platform;
import 'dart:io' show Platform, sleep;
import 'package:example/notification_button.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart' show kIsWeb;

import 'package:clevertap_plugin/clevertap_plugin.dart';
import 'package:example/deeplink_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
import 'package:workmanager/workmanager.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

@pragma('vm:entry-point')
void onKilledStateNotificationClickedHandler(Map<String, dynamic> map) async {
print("onKilledStateNotificationClickedHandler called from headless task!");
print("Notification Payload received: " + map.toString());
}

@pragma('vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
void callbackDispatcher() {
// This is a dummy work manager to test usecases with background isolates

Workmanager().executeTask((task, inputData) {
print("Native started background task: $task");
sleep(Duration(seconds: 30));
print("Native called background task: $task"); //simpleTask will be emitted here.
return Future.value(true);
});
}

Future<void> _firebaseBackgroundMessageHandler(RemoteMessage message) async {
// This is a dummy firebase integration to test usecases with background isolates
await Firebase.initializeApp();
print("_firebaseBackgroundMessageHandler Background");
// CleverTapPlugin.createNotification(jsonEncode(message.data));
}

/// Handles foreground messages of FCM
void _firebaseForegroundMessageHandler(RemoteMessage remoteMessage) {
print('_firebaseForegroundMessageHandler called');
// CleverTapPlugin.createNotification(jsonEncode(remoteMessage.data));
}


void main() async {
WidgetsFlutterBinding.ensureInitialized();
Workmanager().initialize(
callbackDispatcher, // The top level function, aka callbackDispatcher
isInDebugMode: true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
);
Workmanager().registerOneOffTask(
"periodic-task-identifier",
"simplePeriodicTask"
);

await Firebase.initializeApp();
FirebaseMessaging.onMessage.listen(_firebaseForegroundMessageHandler);
FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundMessageHandler);

CleverTapPlugin.onKilledStateNotificationClicked(
onKilledStateNotificationClickedHandler);
runApp(MaterialApp(
Expand Down Expand Up @@ -53,6 +95,7 @@ class _MyAppState extends State<MyApp> {

@override
void initState() {
print("initState");
super.initState();
initPlatformState();
activateCleverTapFlutterPluginHandlers();
Expand Down Expand Up @@ -1731,11 +1774,7 @@ class _MyAppState extends State<MyApp> {
'total': '200',
'payment': 'cash'
};
if (kIsWeb) {
CleverTapPlugin.recordEvent("Charged", chargeDetails);
} else {
CleverTapPlugin.recordChargedEvent(chargeDetails, items);
}
CleverTapPlugin.recordChargedEvent(chargeDetails, items);
showToast("Raised event - Charged");
}

Expand Down
5 changes: 4 additions & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ description: A CleverTap Flutter Example project.
version: 1.0.0+1

environment:
sdk: '>=3.2.0-0 <4.0.0'
sdk: '>=2.12.0 <4.0.0'

dependencies:
flutter_styled_toast: ^2.0.1
Expand All @@ -24,6 +24,9 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.3
workmanager: ^0.5.2
firebase_messaging: ^14.9.2
firebase_core: ^2.31.0

dev_dependencies:
flutter_test:
Expand Down
2 changes: 1 addition & 1 deletion ios/clevertap_plugin.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
Pod::Spec.new do |s|
s.name = 'clevertap_plugin'
s.version = '2.3.1'
s.version = '2.4.0'
s.summary = 'CleverTap Flutter plugin.'
s.description = 'The CleverTap iOS SDK for App Analytics and Engagement.'
s.homepage = 'https://github.com/CleverTap/clevertap-ios-sdk'
Expand Down
2 changes: 1 addition & 1 deletion lib/clevertap_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class CleverTapPlugin {
static const libName = 'Flutter';

static const libVersion =
20301; // If the current version is X.X.X then pass as X0X0X
20400; // If the current version is X.X.X then pass as X0X0X

CleverTapPlugin._internal() {
/// Set the CleverTap Flutter library name and the current version for version tracking
Expand Down
Loading

0 comments on commit 96922e4

Please sign in to comment.