pushwoosh-cordova-plugin 8.3.54 → 8.3.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,13 +14,13 @@ Cross-Platform push notifications by Pushwoosh for Cordova / PhoneGap
14
14
  Using npm:
15
15
 
16
16
  ```
17
- cordova plugin add pushwoosh-cordova-plugin@8.3.54
17
+ cordova plugin add pushwoosh-cordova-plugin@8.3.56
18
18
  ```
19
19
 
20
20
  Using git:
21
21
 
22
22
  ```
23
- cordova plugin add https://github.com/Pushwoosh/pushwoosh-phonegap-plugin.git#8.3.54
23
+ cordova plugin add https://github.com/Pushwoosh/pushwoosh-phonegap-plugin.git#8.3.56
24
24
  ```
25
25
 
26
26
  ### Guide
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pushwoosh-cordova-plugin",
3
- "version": "8.3.54",
3
+ "version": "8.3.56",
4
4
  "description": "\n This plugin allows you to send and receive push notifications. Powered by Pushwoosh (www.pushwoosh.com).\n ",
5
5
  "main":"www/PushNotification.js",
6
6
  "typings":"types/index.d.ts",
package/plugin.xml CHANGED
@@ -1,5 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="pushwoosh-cordova-plugin" version="8.3.54">
2
+ <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="pushwoosh-cordova-plugin" version="8.3.56">
3
3
 
4
4
  <name>Pushwoosh</name>
5
5
 
@@ -65,6 +65,9 @@
65
65
  <source-file src="src/android/src/com/pushwoosh/plugin/pushnotifications/InboxUiStyleManager.java"
66
66
  target-dir="src/com/pushwoosh/plugin/pushnotifications" />
67
67
 
68
+ <source-file src="src/android/src/com/pushwoosh/plugin/pushnotifications/VoIPEventStorage.java"
69
+ target-dir="src/com/pushwoosh/plugin/pushnotifications" />
70
+
68
71
  <source-file src="src/android/src/com/pushwoosh/plugin/pushnotifications/PushwooshNotificationServiceExtension.java"
69
72
  target-dir="src/com/pushwoosh/plugin/pushnotifications" />
70
73
 
@@ -95,13 +98,13 @@
95
98
  <framework src="org.jetbrains.kotlin:kotlin-stdlib:1.1.60" />
96
99
  <framework src="com.google.android.material:material:1.12.0"/>
97
100
 
98
- <framework src="com.pushwoosh:pushwoosh:6.7.46"/>
99
- <framework src="com.pushwoosh:pushwoosh-amazon:6.7.46"/>
100
- <framework src="com.pushwoosh:pushwoosh-firebase:6.7.46"/>
101
- <framework src="com.pushwoosh:pushwoosh-badge:6.7.46"/>
102
- <framework src="com.pushwoosh:pushwoosh-inbox:6.7.46"/>
103
- <framework src="com.pushwoosh:pushwoosh-inbox-ui:6.7.46"/>
104
- <framework src="com.pushwoosh:pushwoosh-huawei:6.7.46"/>
101
+ <framework src="com.pushwoosh:pushwoosh:6.7.48"/>
102
+ <framework src="com.pushwoosh:pushwoosh-amazon:6.7.48"/>
103
+ <framework src="com.pushwoosh:pushwoosh-firebase:6.7.48"/>
104
+ <framework src="com.pushwoosh:pushwoosh-badge:6.7.48"/>
105
+ <framework src="com.pushwoosh:pushwoosh-inbox:6.7.48"/>
106
+ <framework src="com.pushwoosh:pushwoosh-inbox-ui:6.7.48"/>
107
+ <framework src="com.pushwoosh:pushwoosh-huawei:6.7.48"/>
105
108
  </platform>
106
109
 
107
110
  <!-- ios -->
@@ -143,7 +146,7 @@
143
146
  <source url="https://github.com/CocoaPods/Specs.git"/>
144
147
  </config>
145
148
  <pods use-frameworks="true">
146
- <pod name="PushwooshXCFramework" spec="7.0.3" />
149
+ <pod name="PushwooshXCFramework" spec="7.0.6" />
147
150
  <pod name="PushwooshInboxUIXCFramework" spec="7.0.3" />
148
151
  </pods>
149
152
  </podspec>
@@ -16,7 +16,7 @@ def callsSrc = pluginDir ? new File(pluginDir, 'src/android/src/com/pushwoosh/pl
16
16
  def applyVoip = {
17
17
  if (voipEnabled) {
18
18
  println "[${pluginId}] PW_VOIP_ANDROID_ENABLED=true — enabling VoIP (dependency + sources)"
19
- dependencies { implementation "com.pushwoosh:pushwoosh-calls:6.7.46" }
19
+ dependencies { implementation "com.pushwoosh:pushwoosh-calls:6.7.48" }
20
20
  if (callsSrc?.exists()) {
21
21
  android.sourceSets.main.java.srcDirs += callsSrc
22
22
  println "[${pluginId}] Added optional sources: ${callsSrc}"
@@ -11,6 +11,7 @@ public interface CallsAdapter {
11
11
  public boolean unregisterEvent(JSONArray data, final CallbackContext callbackContext);
12
12
  public boolean endCall(JSONArray data, final CallbackContext callbackContext);
13
13
  public boolean initializeVoIPParameters(JSONArray data, final CallbackContext callbackContext);
14
+ public boolean setIncomingCallTimeout(JSONArray data, final CallbackContext callbackContext);
14
15
  public boolean mute();
15
16
  public boolean unmute();
16
17
  public boolean speakerOn();
@@ -49,6 +49,12 @@ public class NoopCallsAdapter implements CallsAdapter{
49
49
  return false;
50
50
  }
51
51
 
52
+ @Override
53
+ public boolean setIncomingCallTimeout(JSONArray data, CallbackContext callbackContext) {
54
+ PWLog.error(TAG,"Method not implemented");
55
+ return false;
56
+ }
57
+
52
58
  @Override
53
59
  public boolean mute() {
54
60
  PWLog.error(TAG,"Method not implemented");
@@ -10,6 +10,7 @@
10
10
 
11
11
  package com.pushwoosh.plugin.pushnotifications;
12
12
 
13
+ import android.content.Context;
13
14
  import android.content.Intent;
14
15
  import android.os.Bundle;
15
16
  import android.os.Handler;
@@ -33,6 +34,7 @@ import com.pushwoosh.inbox.data.InboxMessage;
33
34
  import com.pushwoosh.inbox.exception.InboxMessagesException;
34
35
  import com.pushwoosh.inbox.ui.presentation.view.activity.InboxActivity;
35
36
  import com.pushwoosh.internal.platform.utils.GeneralUtils;
37
+ import com.pushwoosh.internal.platform.AndroidPlatformModule;
36
38
  import com.pushwoosh.internal.utils.JsonUtils;
37
39
  import com.pushwoosh.internal.utils.PWLog;
38
40
  import com.pushwoosh.notification.LocalNotification;
@@ -75,7 +77,6 @@ public class PushNotifications extends CordovaPlugin {
75
77
  private static AtomicBoolean sAppReady = new AtomicBoolean();
76
78
  private static PushNotifications sInstance;
77
79
 
78
- // private CallbackContext callbackContext;
79
80
  private static CordovaInterface cordovaInterface;
80
81
  private static CallsAdapter callsAdapter;
81
82
 
@@ -108,6 +109,25 @@ public class PushNotifications extends CordovaPlugin {
108
109
  return cordovaInterface;
109
110
  }
110
111
 
112
+ /**
113
+ * Checks if Cordova WebView is ready to receive events.
114
+ * Returns false if CordovaInterface is null, Activity is null/finishing/destroyed.
115
+ */
116
+ private static boolean isCordovaReady() {
117
+ if (cordovaInterface == null) {
118
+ return false;
119
+ }
120
+ try {
121
+ android.app.Activity activity = cordovaInterface.getActivity();
122
+ if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
123
+ return false;
124
+ }
125
+ return true;
126
+ } catch (Exception e) {
127
+ return false;
128
+ }
129
+ }
130
+
111
131
  public static HashMap<String, ArrayList<CallbackContext>> getCallbackContextMap() {
112
132
  return callbackContextMap;
113
133
  }
@@ -125,11 +145,26 @@ public class PushNotifications extends CordovaPlugin {
125
145
 
126
146
  @Override
127
147
  public void onDestroy() {
148
+ PWLog.noise(TAG, "onDestroy()");
149
+
128
150
  super.onDestroy();
129
- PWLog.noise("OnDestroy");
130
151
  sAppReady.set(false);
131
152
  }
132
153
 
154
+ @Override
155
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
156
+ PWLog.noise(TAG, "initialize()");
157
+
158
+ cordovaInterface = cordova;
159
+ callsAdapter = CallsAdapterFactory.create(cordova.getActivity().getApplicationContext());
160
+ callbackContextMap.put("answer", new ArrayList<CallbackContext>());
161
+ callbackContextMap.put("reject", new ArrayList<CallbackContext>());
162
+ callbackContextMap.put("hangup", new ArrayList<CallbackContext>());
163
+ callbackContextMap.put("voipPushPayload", new ArrayList<CallbackContext>());
164
+ callbackContextMap.put("voipDidCancelCall", new ArrayList<CallbackContext>());
165
+ callbackContextMap.put("voipDidFailToCancelCall", new ArrayList<CallbackContext>());
166
+ }
167
+
133
168
  private JSONObject getPushFromIntent(Intent intent) {
134
169
  if (null == intent) {
135
170
  return null;
@@ -152,6 +187,8 @@ public class PushNotifications extends CordovaPlugin {
152
187
 
153
188
  @CordovaMethod
154
189
  private boolean onDeviceReady(JSONArray data, CallbackContext callbackContext) {
190
+ PWLog.noise(TAG, "onDeviceReady()");
191
+
155
192
  JSONObject params = null;
156
193
  try {
157
194
  params = data.getJSONObject(0);
@@ -160,34 +197,37 @@ public class PushNotifications extends CordovaPlugin {
160
197
  return false;
161
198
  }
162
199
 
200
+ String appid = null;
163
201
  try {
164
-
165
- String appid = null;
166
202
  if (params.has("appid")) {
167
203
  appid = params.getString("appid");
168
204
  } else {
169
205
  appid = params.getString("pw_appid");
170
206
  }
207
+ } catch (JSONException e) {
208
+ PWLog.error(TAG, "Missing appid parameter. Did you follow the guide correctly?", e);
209
+ return false;
210
+ }
171
211
 
172
- Pushwoosh.getInstance().setAppId(appid);
173
- Pushwoosh.getInstance().setSenderId(params.getString("projectid"));
212
+ String projectid = params.optString("projectid", "");
174
213
 
214
+ Pushwoosh.getInstance().setAppId(appid);
215
+ Pushwoosh.getInstance().setSenderId(projectid);
175
216
 
176
- synchronized (sStartPushLock) {
177
- if (sReceivedPushData != null) {
178
- doOnPushReceived(sReceivedPushData);
179
- }
217
+ try {
218
+ processPendingPushNotifications();
219
+ } catch (Exception e) {
220
+ PWLog.error(TAG, "Failed to process pending push notifications", e);
221
+ }
180
222
 
181
- if (sStartPushData != null) {
182
- doOnPushOpened(sStartPushData);
183
- }
184
- }
223
+ sAppReady.set(true);
185
224
 
186
- sAppReady.set(true);
225
+ try {
226
+ processPersistedVoIPEvents();
187
227
  } catch (Exception e) {
188
- PWLog.error(TAG, "Missing pw_appid parameter. Did you follow the guide correctly?", e);
189
- return false;
228
+ PWLog.error(TAG, "Failed to process persisted VoIP events", e);
190
229
  }
230
+
191
231
  return true;
192
232
  }
193
233
 
@@ -856,18 +896,6 @@ public class PushNotifications extends CordovaPlugin {
856
896
  return true;
857
897
  }
858
898
 
859
- @Override
860
- public void initialize(CordovaInterface cordova, CordovaWebView webView) {
861
- cordovaInterface = cordova;
862
- callsAdapter = CallsAdapterFactory.create(cordova.getActivity().getApplicationContext());
863
- callbackContextMap.put("answer", new ArrayList<CallbackContext>());
864
- callbackContextMap.put("reject", new ArrayList<CallbackContext>());
865
- callbackContextMap.put("hangup", new ArrayList<CallbackContext>());
866
- callbackContextMap.put("voipPushPayload", new ArrayList<CallbackContext>());
867
- callbackContextMap.put("voipDidCancelCall", new ArrayList<CallbackContext>());
868
- callbackContextMap.put("voipDidFailToCancelCall", new ArrayList<CallbackContext>());
869
- }
870
-
871
899
  @Override
872
900
  public boolean execute(String action, JSONArray data, CallbackContext callbackId) {
873
901
  PWLog.debug(TAG, "Plugin Method Called: " + action);
@@ -1103,6 +1131,11 @@ public class PushNotifications extends CordovaPlugin {
1103
1131
  return callsAdapter.initializeVoIPParameters(data, callbackContext);
1104
1132
  }
1105
1133
 
1134
+ @CordovaMethod
1135
+ private boolean setIncomingCallTimeout(JSONArray data, final CallbackContext callbackContext) {
1136
+ return callsAdapter.setIncomingCallTimeout(data, callbackContext);
1137
+ }
1138
+
1106
1139
  @CordovaMethod
1107
1140
  private boolean mute() {
1108
1141
  return callsAdapter.mute();
@@ -1123,17 +1156,75 @@ public class PushNotifications extends CordovaPlugin {
1123
1156
  return callsAdapter.speakerOff();
1124
1157
  }
1125
1158
 
1159
+ private void processPendingPushNotifications() {
1160
+ PWLog.noise(TAG, "processPendingPushNotifications()");
1161
+ synchronized (sStartPushLock) {
1162
+ if (sReceivedPushData != null) {
1163
+ doOnPushReceived(sReceivedPushData);
1164
+ }
1165
+
1166
+ if (sStartPushData != null) {
1167
+ doOnPushOpened(sStartPushData);
1168
+ }
1169
+ }
1170
+ }
1171
+
1172
+ private void processPersistedVoIPEvents() {
1173
+ PWLog.noise(TAG, "processPersistedVoIPEvents()");
1174
+ Context context = AndroidPlatformModule.getApplicationContext();
1175
+ if (context == null) {
1176
+ return;
1177
+ }
1178
+
1179
+ List<VoIPEventStorage.StoredEvent> storedEvents = VoIPEventStorage.loadEvents(context);
1180
+ if (storedEvents.isEmpty()) {
1181
+ return;
1182
+ }
1183
+
1184
+ PWLog.info(TAG, "Processing " + storedEvents.size() + " persisted VoIP events");
1185
+
1186
+ for (VoIPEventStorage.StoredEvent event : storedEvents) {
1187
+ emitVoipEvent(event.type, event.payload);
1188
+ }
1189
+
1190
+ VoIPEventStorage.clearEvents(context);
1191
+ }
1192
+
1193
+ private static void bufferVoipEvent(@NonNull String type, @NonNull JSONObject payload) {
1194
+ Context context = AndroidPlatformModule.getApplicationContext();
1195
+ if (context != null) {
1196
+ VoIPEventStorage.saveEvent(context, type, payload);
1197
+ }
1198
+ }
1199
+
1126
1200
  public static void emitVoipEvent(@NonNull String type, @NonNull JSONObject payload) {
1201
+ PWLog.noise(TAG, "emitVoipEvent(), event type: " + type);
1202
+
1203
+ if (!isCordovaReady()) {
1204
+ PWLog.info(TAG, "Buffering VoIP event '" + type + "': Cordova not ready");
1205
+ bufferVoipEvent(type, payload);
1206
+ return;
1207
+ }
1208
+
1127
1209
  ArrayList<CallbackContext> callbackContexts = getCallbackContexts().get(type);
1128
- if (callbackContexts == null) return;
1210
+ if (callbackContexts != null && !callbackContexts.isEmpty()) {
1211
+ for (final CallbackContext callbackContext : callbackContexts) {
1212
+ getCordovaInterface().getThreadPool().execute(() -> {
1213
+ PluginResult result = new PluginResult(PluginResult.Status.OK, payload);
1214
+ result.setKeepCallback(true);
1215
+ callbackContext.sendPluginResult(result);
1216
+ });
1217
+ }
1218
+ return;
1219
+ }
1129
1220
 
1130
- for (final CallbackContext callbackContext : callbackContexts) {
1131
- getCordovaInterface().getThreadPool().execute(() -> {
1132
- PluginResult result = new PluginResult(PluginResult.Status.OK, payload);
1133
- result.setKeepCallback(true);
1134
- callbackContext.sendPluginResult(result);
1135
- });
1221
+ if (!sAppReady.get()) {
1222
+ PWLog.info(TAG, "Buffering VoIP event '" + type + "': JS not ready");
1223
+ bufferVoipEvent(type, payload);
1224
+ return;
1136
1225
  }
1226
+
1227
+ PWLog.warn(TAG, "No callback contexts registered for event: " + type);
1137
1228
  }
1138
1229
 
1139
1230
  private static JSONObject inboxMessageToJson(InboxMessage message) {
@@ -1160,4 +1251,4 @@ public class PushNotifications extends CordovaPlugin {
1160
1251
  }
1161
1252
  return object;
1162
1253
  }
1163
- }
1254
+ }
@@ -0,0 +1,130 @@
1
+ package com.pushwoosh.plugin.pushnotifications;
2
+
3
+ import android.content.Context;
4
+ import android.content.SharedPreferences;
5
+ import androidx.annotation.NonNull;
6
+ import com.pushwoosh.internal.utils.PWLog;
7
+ import org.json.JSONArray;
8
+ import org.json.JSONObject;
9
+ import java.util.ArrayList;
10
+ import java.util.List;
11
+
12
+ /**
13
+ * VoIP Event Buffering Storage
14
+ *
15
+ * Provides persistent storage for VoIP events when WebView/JavaScript is not ready.
16
+ */
17
+ public class VoIPEventStorage {
18
+ private static final String TAG = "VoIPEventStorage";
19
+ private static final String PREFS_NAME = "pushwoosh_voip_events";
20
+ private static final String KEY_BUFFERED_EVENTS = "buffered_events";
21
+ private static final long TTL_MILLIS = 24 * 60 * 60 * 1000; // 24 hours
22
+ private static final Object STORAGE_LOCK = new Object();
23
+
24
+ /**
25
+ * Represents a stored VoIP event with metadata
26
+ */
27
+ public static class StoredEvent {
28
+ public String type;
29
+ public JSONObject payload;
30
+ public long timestamp;
31
+
32
+ public StoredEvent(String type, JSONObject payload, long timestamp) {
33
+ this.type = type;
34
+ this.payload = payload;
35
+ this.timestamp = timestamp;
36
+ }
37
+
38
+ public boolean isExpired() {
39
+ return (System.currentTimeMillis() - timestamp) > TTL_MILLIS;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Save VoIP event to persistent storage
45
+ */
46
+ public static boolean saveEvent(@NonNull Context context, @NonNull String type, @NonNull JSONObject payload) {
47
+ PWLog.noise(TAG, "saveEvent()");
48
+
49
+ try {
50
+ synchronized (STORAGE_LOCK) {
51
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
52
+ String existingData = prefs.getString(KEY_BUFFERED_EVENTS, "[]");
53
+ JSONArray events = new JSONArray(existingData);
54
+
55
+ JSONObject event = new JSONObject();
56
+ event.put("type", type);
57
+ event.put("payload", payload);
58
+ event.put("timestamp", System.currentTimeMillis());
59
+
60
+ events.put(event);
61
+
62
+ boolean success = prefs.edit()
63
+ .putString(KEY_BUFFERED_EVENTS, events.toString())
64
+ .commit();
65
+
66
+ return success;
67
+ }
68
+
69
+ } catch (Exception e) {
70
+ PWLog.error(TAG, "Failed to save VoIP event", e);
71
+ return false;
72
+ }
73
+ }
74
+
75
+ private static StoredEvent parseStoredEvent(@NonNull JSONObject eventObj) throws Exception {
76
+ String type = eventObj.getString("type");
77
+ JSONObject payload = eventObj.getJSONObject("payload");
78
+ long timestamp = eventObj.getLong("timestamp");
79
+ return new StoredEvent(type, payload, timestamp);
80
+ }
81
+
82
+ /**
83
+ * Load all non-expired events from storage.
84
+ */
85
+ public static List<StoredEvent> loadEvents(@NonNull Context context) {
86
+ PWLog.noise(TAG, "loadEvents()");
87
+
88
+ List<StoredEvent> result = new ArrayList<>();
89
+
90
+ try {
91
+ synchronized (STORAGE_LOCK) {
92
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
93
+ String data = prefs.getString(KEY_BUFFERED_EVENTS, "[]");
94
+ JSONArray events = new JSONArray(data);
95
+
96
+ for (int i = 0; i < events.length(); i++) {
97
+ try {
98
+ StoredEvent event = parseStoredEvent(events.getJSONObject(i));
99
+
100
+ if (event.isExpired()) {
101
+ continue;
102
+ }
103
+
104
+ result.add(event);
105
+ } catch (Exception e) {
106
+ PWLog.error(TAG, "Failed to parse VoIP event at index " + i + ", skipping", e);
107
+ }
108
+ }
109
+ }
110
+
111
+ } catch (Exception e) {
112
+ PWLog.error(TAG, "Failed to load VoIP events", e);
113
+ }
114
+
115
+ return result;
116
+ }
117
+
118
+ public static void clearEvents(@NonNull Context context) {
119
+ PWLog.noise(TAG, "clearEvents()");
120
+
121
+ try {
122
+ synchronized (STORAGE_LOCK) {
123
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
124
+ prefs.edit().remove(KEY_BUFFERED_EVENTS).commit();
125
+ }
126
+ } catch (Exception e) {
127
+ PWLog.error(TAG, "Failed to clear VoIP events", e);
128
+ }
129
+ }
130
+ }
@@ -13,82 +13,109 @@ import com.pushwoosh.internal.platform.AndroidPlatformModule;
13
13
  import com.pushwoosh.internal.utils.PWLog;
14
14
 
15
15
  public class PWCordovaCallEventListener implements CallEventListener {
16
+
17
+ private static final String TAG = "PWCordovaCallEventListener";
16
18
  private static final Object sCurrentCallLock = new Object();
17
19
  private static Bundle currentCallInfo = null;
18
20
 
21
+ private void setCurrentCallInfo(Bundle bundle) {
22
+ synchronized (sCurrentCallLock) {
23
+ currentCallInfo = bundle;
24
+ }
25
+ }
19
26
 
20
- @Override
21
- public void onAnswer(@NonNull PushwooshVoIPMessage pushwooshVoIPMessage, int i) {
27
+ private void clearCurrentCallInfo() {
22
28
  synchronized (sCurrentCallLock) {
23
- currentCallInfo = pushwooshVoIPMessage.getRawPayload();
29
+ currentCallInfo = null;
24
30
  }
25
-
31
+ }
32
+
33
+ public static Bundle getCurrentCallInfo() {
34
+ synchronized (sCurrentCallLock) {
35
+ return currentCallInfo;
36
+ }
37
+ }
38
+
39
+ private void launchMainActivity() {
40
+ PWLog.noise(TAG, "launchMainActivity()");
26
41
  try {
27
42
  Context context = AndroidPlatformModule.getApplicationContext();
28
- Intent launchIntent = context != null ? context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()) : null;
29
-
30
- if (launchIntent != null) {
31
- launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
32
- context.startActivity(launchIntent);
33
- PWLog.info("PWCordovaCallEventListener", "Launched main activity for call");
43
+ if (context == null) {
44
+ PWLog.error(TAG, "cant launch activity: context is null");
45
+ return;
46
+ }
47
+ Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
48
+ if (launchIntent == null) {
49
+ PWLog.error(TAG, "cant launch activity: launchIntent is null");
50
+ return;
34
51
  }
52
+
53
+ launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
54
+ context.startActivity(launchIntent);
35
55
  } catch (Exception e) {
36
- PWLog.error("PWCordovaCallEventListener", "Failed to launch activity", e);
56
+ PWLog.error(TAG, "Failed to launch activity", e);
37
57
  }
58
+ }
59
+
60
+
61
+ @Override
62
+ public void onCreateIncomingConnection(@Nullable Bundle bundle) {
63
+ PWLog.noise(TAG, "onCreateIncomingConnection()");
64
+
65
+ setCurrentCallInfo(bundle);
66
+ PushwooshCallsAdapter.onCreateIncomingConnection(bundle);
67
+ }
38
68
 
69
+
70
+ @Override
71
+ public void onAnswer(@NonNull PushwooshVoIPMessage pushwooshVoIPMessage, int i) {
72
+ PWLog.noise(TAG, "onAnswer()");
73
+
74
+ setCurrentCallInfo(pushwooshVoIPMessage.getRawPayload());
39
75
  PushwooshCallsAdapter.onAnswer(pushwooshVoIPMessage);
76
+ launchMainActivity();
40
77
  }
41
78
 
42
79
  @Override
43
80
  public void onReject(@NonNull PushwooshVoIPMessage pushwooshVoIPMessage) {
81
+ PWLog.noise(TAG, "onReject()");
82
+
44
83
  PushwooshCallsAdapter.onReject(pushwooshVoIPMessage);
45
- synchronized (sCurrentCallLock) {
46
- currentCallInfo = null;
47
- }
84
+ clearCurrentCallInfo();
48
85
  }
49
86
 
50
87
  @Override
51
88
  public void onDisconnect(@NonNull PushwooshVoIPMessage pushwooshVoIPMessage) {
52
- PushwooshCallsAdapter.onDisconnect(pushwooshVoIPMessage);
53
- synchronized (sCurrentCallLock) {
54
- currentCallInfo = null;
55
- }
56
- }
89
+ PWLog.noise(TAG, "onDisconnect()");
57
90
 
58
- @Override
59
- public void onCreateIncomingConnection(@Nullable Bundle bundle) {
60
- synchronized (sCurrentCallLock) {
61
- currentCallInfo = bundle;
62
- }
63
- PushwooshCallsAdapter.onCreateIncomingConnection(bundle);
91
+ PushwooshCallsAdapter.onDisconnect(pushwooshVoIPMessage);
92
+ clearCurrentCallInfo();
64
93
  }
65
94
 
66
95
  @Override
67
96
  public void onCallAdded(@NonNull PushwooshVoIPMessage pushwooshVoIPMessage) {
97
+ PWLog.noise(TAG, "onCallAdded()");
68
98
  //stub
69
99
  }
70
100
 
71
101
  @Override
72
102
  public void onCallRemoved(@NonNull PushwooshVoIPMessage pushwooshVoIPMessage) {
103
+ PWLog.noise(TAG, "onCallRemoved()");
73
104
  //stub
74
105
  }
75
106
 
76
107
  @Override
77
108
  public void onCallCancelled(@NonNull PushwooshVoIPMessage pushwooshVoIPMessage) {
109
+ PWLog.noise(TAG, "onCallCancelled()");
110
+
78
111
  PushwooshCallsAdapter.onCallCancelled(pushwooshVoIPMessage);
79
- synchronized (sCurrentCallLock) {
80
- currentCallInfo = null;
81
- }
112
+ clearCurrentCallInfo();
82
113
  }
83
114
 
84
115
  @Override
85
116
  public void onCallCancellationFailed(@Nullable String callId, @Nullable String reason) {
86
- PushwooshCallsAdapter.onCallCancellationFailed(callId, reason);
87
- }
117
+ PWLog.noise(TAG, "onCallCancellationFailed()");
88
118
 
89
- public static Bundle getCurrentCallInfo() {
90
- synchronized (sCurrentCallLock) {
91
- return currentCallInfo;
92
- }
119
+ PushwooshCallsAdapter.onCallCancellationFailed(callId, reason);
93
120
  }
94
121
  }
@@ -9,6 +9,7 @@ import android.media.AudioManager;
9
9
  import android.os.Bundle;
10
10
 
11
11
  import com.pushwoosh.Pushwoosh;
12
+ import com.pushwoosh.calls.CallPermissionsCallback;
12
13
  import com.pushwoosh.calls.PushwooshCallReceiver;
13
14
  import com.pushwoosh.calls.PushwooshCallSettings;
14
15
  import com.pushwoosh.calls.PushwooshVoIPMessage;
@@ -29,6 +30,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
29
30
 
30
31
  @Override
31
32
  public boolean setVoipAppCode(JSONArray data, CallbackContext callbackContext) {
33
+ PWLog.noise(TAG, "setVoipAppCode()");
32
34
  try {
33
35
  String appCode = data.getString(0);
34
36
  Pushwoosh.getInstance().addAlternativeAppCode(appCode);
@@ -40,11 +42,18 @@ public class PushwooshCallsAdapter implements CallsAdapter {
40
42
  }
41
43
 
42
44
  @Override
43
- public boolean requestCallPermission(JSONArray data, CallbackContext callbackContext) {
45
+ public boolean requestCallPermission(JSONArray data, final CallbackContext callbackContext) {
46
+ PWLog.noise(TAG, "requestCallPermission()");
44
47
  try {
45
- PushwooshCallSettings.requestCallPermissions();
48
+ PushwooshCallSettings.requestCallPermissions(new CallPermissionsCallback() {
49
+ @Override
50
+ public void onPermissionResult(boolean granted, java.util.List<String> grantedPerms, java.util.List<String> deniedPerms) {
51
+ callbackContext.success(granted ? 1 : 0);
52
+ }
53
+ });
46
54
  } catch (Exception e) {
47
55
  PWLog.error(TAG, "Failed to request call permissions: " + e.getMessage());
56
+ callbackContext.error("Failed to request call permissions: " + e.getMessage());
48
57
  return false;
49
58
  }
50
59
  return true;
@@ -52,6 +61,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
52
61
 
53
62
  @Override
54
63
  public boolean getCallPermissionStatus(JSONArray data, CallbackContext callbackContext) {
64
+ PWLog.noise(TAG, "getCallPermissionStatus()");
55
65
  try {
56
66
  int status = PushwooshCallSettings.getCallPermissionStatus();
57
67
  callbackContext.success(status);
@@ -64,6 +74,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
64
74
 
65
75
  @Override
66
76
  public boolean registerEvent(JSONArray data, CallbackContext callbackContext) {
77
+ PWLog.noise(TAG, "registerEvent()");
67
78
  try {
68
79
 
69
80
  String eventType = data.getString(0);
@@ -79,6 +90,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
79
90
 
80
91
  @Override
81
92
  public boolean unregisterEvent(JSONArray data, CallbackContext callbackContext) {
93
+ PWLog.noise(TAG, "unregisterEvent()");
82
94
  try {
83
95
  String eventType = data.getString(0);
84
96
  ArrayList<CallbackContext> callbackContextList = getCallbackContextMap().get(eventType);
@@ -97,6 +109,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
97
109
 
98
110
  @Override
99
111
  public boolean endCall(JSONArray data, CallbackContext callbackContext) {
112
+ PWLog.noise(TAG, "endCall()");
100
113
  Context context = AndroidPlatformModule.getApplicationContext();
101
114
  Intent endCallIntent = new Intent(context, PushwooshCallReceiver.class);
102
115
  endCallIntent.putExtras(PWCordovaCallEventListener.getCurrentCallInfo());
@@ -108,6 +121,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
108
121
 
109
122
  @Override
110
123
  public boolean initializeVoIPParameters(JSONArray data, CallbackContext callbackContext) {
124
+ PWLog.noise(TAG, "initializeVoIPParameters()");
111
125
  try {
112
126
  String callSound = data.getString(1);
113
127
  if (callSound!= null && !callSound.isEmpty()){
@@ -120,8 +134,22 @@ public class PushwooshCallsAdapter implements CallsAdapter {
120
134
  }
121
135
  }
122
136
 
137
+ @Override
138
+ public boolean setIncomingCallTimeout(JSONArray data, CallbackContext callbackContext) {
139
+ PWLog.noise(TAG, "setIncomingCallTimeout()");
140
+ try {
141
+ double timeout = data.getDouble(0);
142
+ PushwooshCallSettings.setIncomingCallTimeout(timeout);
143
+ return true;
144
+ } catch (Exception e) {
145
+ PWLog.error("Failed to set incoming call timeout: " + e.getMessage());
146
+ return false;
147
+ }
148
+ }
149
+
123
150
  @Override
124
151
  public boolean mute() {
152
+ PWLog.noise(TAG, "mute()");
125
153
  try {
126
154
  AudioManager audioManager = (AudioManager) getCordovaInterface().getActivity().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
127
155
  audioManager.setMicrophoneMute(true);
@@ -134,6 +162,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
134
162
 
135
163
  @Override
136
164
  public boolean unmute() {
165
+ PWLog.noise(TAG, "unmute()");
137
166
  try {
138
167
  AudioManager audioManager = (AudioManager) getCordovaInterface().getActivity().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
139
168
  audioManager.setMicrophoneMute(false);
@@ -146,6 +175,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
146
175
 
147
176
  @Override
148
177
  public boolean speakerOn() {
178
+ PWLog.noise(TAG, "speakerOn()");
149
179
  try {
150
180
  AudioManager audioManager = (AudioManager) getCordovaInterface().getActivity().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
151
181
  audioManager.setSpeakerphoneOn(true);
@@ -158,6 +188,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
158
188
 
159
189
  @Override
160
190
  public boolean speakerOff() {
191
+ PWLog.noise(TAG, "speakerOff()");
161
192
  try {
162
193
  AudioManager audioManager = (AudioManager) getCordovaInterface().getActivity().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
163
194
  audioManager.setSpeakerphoneOn(false);
@@ -169,27 +200,33 @@ public class PushwooshCallsAdapter implements CallsAdapter {
169
200
  }
170
201
 
171
202
  public static void onAnswer(PushwooshVoIPMessage voIPMessage) {
203
+ PWLog.noise(TAG, "onAnswer()");
172
204
  PushNotifications.emitVoipEvent("answer", parseVoIPMessage(voIPMessage));
173
205
  }
174
206
 
175
207
  public static void onReject(PushwooshVoIPMessage voIPMessage) {
208
+ PWLog.noise(TAG, "onReject()");
176
209
  PushNotifications.emitVoipEvent("reject", parseVoIPMessage(voIPMessage));
177
210
  }
178
211
 
179
212
  public static void onDisconnect(PushwooshVoIPMessage voIPMessage) {
213
+ PWLog.noise(TAG, "onDisconnect()");
180
214
  PushNotifications.emitVoipEvent("hangup", parseVoIPMessage(voIPMessage));
181
215
  }
182
216
 
183
217
  public static void onCreateIncomingConnection(Bundle bundle) {
218
+ PWLog.noise(TAG, "onCreateIncomingConnection()");
184
219
  PushwooshVoIPMessage voipMessage = new PushwooshVoIPMessage(bundle);
185
220
  PushNotifications.emitVoipEvent("voipPushPayload", parseVoIPMessage(voipMessage));
186
221
  }
187
222
 
188
223
  public static void onCallCancelled(PushwooshVoIPMessage voIPMessage) {
224
+ PWLog.noise(TAG, "onCallCancelled()");
189
225
  PushNotifications.emitVoipEvent("voipDidCancelCall", parseVoIPMessage(voIPMessage));
190
226
  }
191
227
 
192
228
  public static void onCallCancellationFailed(String callId, String reason) {
229
+ PWLog.noise(TAG, "onCallCancellationFailed()");
193
230
  org.json.JSONObject payload = new org.json.JSONObject();
194
231
  try {
195
232
  payload.put("callId", callId != null ? callId : "");
@@ -199,6 +236,7 @@ public class PushwooshCallsAdapter implements CallsAdapter {
199
236
  }
200
237
 
201
238
  private static org.json.JSONObject parseVoIPMessage(PushwooshVoIPMessage message) {
239
+ PWLog.noise(TAG, "parseVoIPMessage()");
202
240
  org.json.JSONObject payload = new org.json.JSONObject();
203
241
  try {
204
242
  Bundle rawBundle = message.getRawPayload();
@@ -634,7 +634,9 @@ API_AVAILABLE(ios(10.0)) {
634
634
  #if PW_VOIP_ENABLED
635
635
  // MARK: - Voip settings
636
636
  - (void)requestCallPermission:(CDVInvokedUrlCommand *)command {
637
- //stub, android only
637
+ //stub, android only - iOS doesn't require call permission
638
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES];
639
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
638
640
  }
639
641
 
640
642
  - (void)getCallPermissionStatus:(CDVInvokedUrlCommand *)command {
@@ -725,6 +727,28 @@ API_AVAILABLE(ios(10.0)) {
725
727
  [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
726
728
  }
727
729
 
730
+ // MARK: - Set Incoming Call Timeout
731
+ - (void)setIncomingCallTimeout:(CDVInvokedUrlCommand *)command {
732
+ CDVPluginResult* pluginResult = nil;
733
+
734
+ NSNumber *timeoutNumber = [command.arguments objectAtIndex:0];
735
+
736
+ if ([timeoutNumber isKindOfClass:[NSNumber class]]) {
737
+ NSTimeInterval timeout = [timeoutNumber doubleValue];
738
+
739
+ if (@available(iOS 14.0, *)) {
740
+ [PushwooshVoIPImplementation setIncomingCallTimeout:timeout];
741
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"Incoming call timeout set"];
742
+ } else {
743
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"VoIP requires iOS 14.0 or later"];
744
+ }
745
+ } else {
746
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid timeout parameter"];
747
+ }
748
+
749
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
750
+ }
751
+
728
752
  // MARK: - Incoming Call Payload
729
753
  - (void)voipDidReceiveIncomingCallWithPayload:(PWVoIPMessage *)payload {
730
754
  for (id callbackId in callbackIds[@"voipPushPayload"]) {
@@ -28,6 +28,7 @@ type InboxNotification = {
28
28
  isActionPerformed?: boolean;
29
29
  }
30
30
 
31
+
31
32
  type AuthOptions = Record<string, number|string>
32
33
 
33
34
  type RemoteNotificationStatus = Record<string,string|number|boolean>
@@ -95,7 +96,7 @@ export interface PushNotification {
95
96
  speakerOff(success?: () => void, error?: (err: Error | string) => void): void;
96
97
  mute(success?: () => void, error?: (err: Error | string) => void): void;
97
98
  unmute(success?: () => void, error?: (err: Error | string) => void): void;
98
- requestCallPermission(): void;
99
+ requestCallPermission(success?: (granted: boolean) => void, error?: (err: Error | string) => void): void;
99
100
  getCallPermissionStatus(success?: (status: number) => void, error?: (err: Error | string) => void): void;
100
101
  endCall(success?: () => void, error?: (err: Error | string) => void): void;
101
102
  }
@@ -641,9 +641,11 @@ PushNotification.prototype.unmute = function(success, error) {
641
641
  // Android calls
642
642
  /**
643
643
  * Request call permission and register phone account
644
+ * @param {Function} success - Callback invoked with permission result
645
+ * @param {Function} error - Callback invoked if the operation fails
644
646
  */
645
- PushNotification.prototype.requestCallPermission = function() {
646
- exec(null, null, "PushNotification", "requestCallPermission", []);
647
+ PushNotification.prototype.requestCallPermission = function(success, error) {
648
+ exec(success, error, "PushNotification", "requestCallPermission", []);
647
649
  }
648
650
 
649
651
  /**
@@ -833,4 +835,8 @@ PushNotification.prototype.setVoipAppCode = function(appCode) {
833
835
  exec(null, null, "PushNotification", "setVoipAppCode", [appCode]);
834
836
  }
835
837
 
838
+ PushNotification.prototype.setIncomingCallTimeout = function(timeoutSeconds) {
839
+ exec(null, null, "PushNotification", "setIncomingCallTimeout", [timeoutSeconds]);
840
+ }
841
+
836
842
  module.exports = new PushNotification();