From 6f6462b5e05f953b4ec4caecb483ca9bac8a51f1 Mon Sep 17 00:00:00 2001
From: Vassil Angelov
Date: Wed, 20 Dec 2023 14:58:50 +0200
Subject: [PATCH 01/29] Extract API calls from NetworkManager
---
.../com/clevertap/android/sdk/CTXtensions.kt | 13 +-
.../clevertap/android/sdk/CleverTapAPI.java | 2 +-
.../android/sdk/CleverTapFactory.java | 14 +-
.../clevertap/android/sdk/InAppFCManager.java | 23 +-
.../sdk/network/NetworkHeadersListener.kt | 13 +
.../android/sdk/network/NetworkManager.java | 581 ++++++------------
.../sdk/network/SSLContextBuilder.java | 37 --
.../android/sdk/network/api/CtApi.kt | 116 ++++
.../android/sdk/network/api/CtApiProvider.kt | 35 ++
.../sdk/network/api/SendQueueRequestBody.kt | 14 +
.../android/sdk/network/http/CtHttpClient.kt | 6 +
.../android/sdk/network/http/Request.kt | 5 +
.../android/sdk/network/http/Response.kt | 32 +
.../network/http/UrlConnectionHttpClient.kt | 101 +++
.../android/sdk/LocalDataStoreProvider.kt | 15 +
.../android/sdk/network/NetworkManagerTest.kt | 154 +++++
.../android/sdk/network/api/CtApiTest.kt | 87 +++
.../sdk/network/api/CtApiTestProvider.kt | 60 ++
.../sdk/network/http/MockHttpClient.kt | 19 +
gradle/libs.versions.toml | 2 +-
20 files changed, 880 insertions(+), 449 deletions(-)
delete mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/SSLContextBuilder.java
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApi.kt
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApiProvider.kt
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/SendQueueRequestBody.kt
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/CtHttpClient.kt
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Request.kt
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Response.kt
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/UrlConnectionHttpClient.kt
create mode 100644 clevertap-core/src/test/java/com/clevertap/android/sdk/LocalDataStoreProvider.kt
create mode 100644 clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt
create mode 100644 clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt
create mode 100644 clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTestProvider.kt
create mode 100644 clevertap-core/src/test/java/com/clevertap/android/sdk/network/http/MockHttpClient.kt
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTXtensions.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTXtensions.kt
index 1d848a36d..2a633e758 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CTXtensions.kt
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CTXtensions.kt
@@ -16,6 +16,7 @@ import androidx.core.app.NotificationManagerCompat
import com.clevertap.android.sdk.events.EventGroup.PUSH_NOTIFICATION_VIEWED
import com.clevertap.android.sdk.task.CTExecutorFactory
import org.json.JSONArray
+import org.json.JSONException
import org.json.JSONObject
fun Context.isPackageAndOsTargetsAbove(apiLevel: Int) =
@@ -272,4 +273,14 @@ fun String?.concatIfNotNull(other: String?, separator: String = ""): String? {
*/
fun Location.isValid(): Boolean {
return this.latitude in -90.0..90.0 && this.longitude in -180.0..180.0
-}
\ No newline at end of file
+}
+
+fun String?.toJsonOrNull(): JSONObject? {
+ return this?.let {
+ try {
+ JSONObject(it)
+ } catch (e: JSONException) {
+ null
+ }
+ }
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
index 5acc39073..39324502b 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
@@ -1336,7 +1336,7 @@ public void setSCDomainListener(SCDomainListener scDomainListener) {
if(coreState.getNetworkManager() != null) {
NetworkManager networkManager = (NetworkManager) coreState.getNetworkManager();
- String domain = networkManager.getDomainFromPrefsOrMetadata(EventGroup.REGULAR);
+ String domain = networkManager.getDomain(EventGroup.REGULAR);
if(domain != null) {
scDomainListener.onSCDomainAvailable(getSCDomain(domain));
}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.java
index 3f51117f5..d82d8bb4c 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapFactory.java
@@ -24,6 +24,7 @@
import com.clevertap.android.sdk.network.CompositeBatchListener;
import com.clevertap.android.sdk.network.FetchInAppListener;
import com.clevertap.android.sdk.network.NetworkManager;
+import com.clevertap.android.sdk.network.api.CtApiProviderKt;
import com.clevertap.android.sdk.pushnotification.PushProviders;
import com.clevertap.android.sdk.pushnotification.work.CTWorkManager;
import com.clevertap.android.sdk.response.InAppResponse;
@@ -67,11 +68,13 @@ static CoreState getCoreState(Context context, CleverTapInstanceConfig cleverTap
DBManager baseDatabaseManager = new DBManager(config, ctLockManager);
coreState.setDatabaseManager(baseDatabaseManager);
- CryptHandler cryptHandler = new CryptHandler(config.getEncryptionLevel(), CryptHandler.EncryptionAlgorithm.AES, config.getAccountId());
+ CryptHandler cryptHandler = new CryptHandler(config.getEncryptionLevel(),
+ CryptHandler.EncryptionAlgorithm.AES, config.getAccountId());
coreState.setCryptHandler(cryptHandler);
Task task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
task.execute("migratingEncryptionLevel", () -> {
- CryptUtils.migrateEncryptionLevel(context, config, cryptHandler, baseDatabaseManager.loadDBAdapter(context));
+ CryptUtils.migrateEncryptionLevel(context, config, cryptHandler,
+ baseDatabaseManager.loadDBAdapter(context));
return null;
});
@@ -84,7 +87,7 @@ static CoreState getCoreState(Context context, CleverTapInstanceConfig cleverTap
DeviceInfo deviceInfo = new DeviceInfo(context, config, cleverTapID, coreMetaData);
coreState.setDeviceInfo(deviceInfo);
- CTPreferenceCache.getInstance(context,config);
+ CTPreferenceCache.getInstance(context, config);
BaseCallbackManager callbackManager = new CallbackManager(config, deviceInfo);
coreState.setCallbackManager(callbackManager);
@@ -190,6 +193,7 @@ public Void call() throws Exception {
validationResultStack,
controllerManager,
baseDatabaseManager,
+ CtApiProviderKt.provideDefaultTestCtApi(context, config, deviceInfo),
callbackManager,
ctLockManager,
validator,
@@ -282,11 +286,11 @@ public Void call() throws Exception {
LocationManager locationManager = new LocationManager(context, config, coreMetaData, baseEventQueueManager);
coreState.setLocationManager(locationManager);
- CTWorkManager ctWorkManager = new CTWorkManager(context,config);
+ CTWorkManager ctWorkManager = new CTWorkManager(context, config);
PushProviders pushProviders = PushProviders
.load(context, config, baseDatabaseManager, validationResultStack,
- analyticsManager, controllerManager,ctWorkManager);
+ analyticsManager, controllerManager, ctWorkManager);
coreState.setPushProviders(pushProviders);
ActivityLifeCycleManager activityLifeCycleManager = new ActivityLifeCycleManager(context, config,
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java
index 82c841689..75328ce4d 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppFCManager.java
@@ -122,21 +122,20 @@ public void didShow(final Context context, CTInAppNotification inapp) {
++shownToday);
}
- public void attachToHeader(final Context context, JSONObject header) {
- try {
- // Trigger reset for dates
-
- header.put("imp", getIntFromPrefs(getKeyWithDeviceId(Constants.KEY_COUNTS_SHOWN_TODAY, deviceId), 0));
+ public int getShownTodayCount() {
+ return getIntFromPrefs(getKeyWithDeviceId(Constants.KEY_COUNTS_SHOWN_TODAY, deviceId), 0);
+ }
+ public JSONArray getInAppsCount(final Context context) {
+ try {
// tlc: [[targetID, todayCount, lifetime]]
JSONArray arr = new JSONArray();
final SharedPreferences prefs = StorageHelper
.getPreferences(context, storageKeyWithSuffix(getKeyWithDeviceId(Constants.KEY_COUNTS_PER_INAPP, deviceId)));
final Map all = prefs.getAll();
- for (String inapp : all.keySet()) {
- final Object o = all.get(inapp);
- if (o instanceof String) {
- final String[] parts = ((String) o).split(",");
+ for (Map.Entry inapp : all.entrySet()) {
+ if (inapp.getValue() instanceof String) {
+ final String[] parts = ((String) inapp.getValue()).split(",");
if (parts.length == 2) {
JSONArray a = new JSONArray();
a.put(0, inapp);
@@ -146,10 +145,10 @@ public void attachToHeader(final Context context, JSONObject header) {
}
}
}
-
- header.put("tlc", arr);
+ return arr;
} catch (Throwable t) {
- Logger.v("Failed to attach FC to header", t);
+ Logger.v("Failed to get in apps count", t);
+ return null;
}
}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkHeadersListener.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkHeadersListener.kt
index 9bec8f964..4c879713e 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkHeadersListener.kt
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkHeadersListener.kt
@@ -1,5 +1,9 @@
package com.clevertap.android.sdk.network
+import com.clevertap.android.sdk.events.EventGroup
+import com.clevertap.android.sdk.events.EventGroup.PUSH_NOTIFICATION_VIEWED
+import com.clevertap.android.sdk.events.EventGroup.REGULAR
+import com.clevertap.android.sdk.events.EventGroup.VARIABLES
import org.json.JSONObject
interface NetworkHeadersListener {
@@ -20,5 +24,14 @@ enum class EndpointId(val identifier: String) {
fun fromString(identifier: String): EndpointId {
return values().find { identifier.contains(it.identifier) } ?: ENDPOINT_A1
}
+
+ @JvmStatic
+ fun fromEventGroup(eventGroup: EventGroup): EndpointId {
+ return when (eventGroup) {
+ PUSH_NOTIFICATION_VIEWED -> ENDPOINT_SPIKY
+ REGULAR -> ENDPOINT_A1
+ VARIABLES -> ENDPOINT_DEFINE_VARS
+ }
+ }
}
}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java
index 049a31f0a..575c9c640 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java
@@ -31,6 +31,9 @@
import com.clevertap.android.sdk.events.EventGroup;
import com.clevertap.android.sdk.interfaces.NotificationRenderedListener;
import com.clevertap.android.sdk.login.IdentityRepoFactory;
+import com.clevertap.android.sdk.network.api.CtApi;
+import com.clevertap.android.sdk.network.api.SendQueueRequestBody;
+import com.clevertap.android.sdk.network.http.Response;
import com.clevertap.android.sdk.pushnotification.PushNotificationUtil;
import com.clevertap.android.sdk.response.ARPResponse;
import com.clevertap.android.sdk.response.CleverTapResponse;
@@ -49,35 +52,28 @@
import com.clevertap.android.sdk.task.Task;
import com.clevertap.android.sdk.validation.ValidationResultStack;
import com.clevertap.android.sdk.validation.Validator;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@RestrictTo(Scope.LIBRARY)
public class NetworkManager extends BaseNetworkManager {
- private static SSLSocketFactory sslSocketFactory;
- private static SSLContext sslContext;
+
private final BaseCallbackManager callbackManager;
private final List cleverTapResponses = new ArrayList<>();
private final CleverTapInstanceConfig config;
+
private final Context context;
+
private final ControllerManager controllerManager;
+
private final CoreMetaData coreMetaData;
- private int currentRequestTimestamp = 0;
private final BaseDatabaseManager databaseManager;
@@ -87,12 +83,14 @@ public class NetworkManager extends BaseNetworkManager {
private final Logger logger;
+ private final CtApi ctApi;
+
+ private int responseFailureCount = 0;
+
private int networkRetryCount = 0;
private final ValidationResultStack validationResultStack;
- private int responseFailureCount = 0;
-
private final Validator validator;
private int minDelayFrequency = 0;
@@ -118,7 +116,7 @@ public static boolean isNetworkOnline(Context context) {
}
@SuppressLint("MissingPermission") NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnected();
- } catch (Throwable ignore) {
+ } catch (Exception ignore) {
// lets be optimistic, if we are truly offline we handle the exception
return true;
}
@@ -132,6 +130,7 @@ public NetworkManager(
ValidationResultStack validationResultStack,
ControllerManager controllerManager,
BaseDatabaseManager baseDatabaseManager,
+ CtApi ctApi,
final BaseCallbackManager callbackManager,
CTLockManager ctLockManager,
Validator validator,
@@ -150,6 +149,7 @@ public NetworkManager(
this.validationResultStack = validationResultStack;
this.controllerManager = controllerManager;
databaseManager = baseDatabaseManager;
+ this.ctApi = ctApi;
cleverTapResponses.add(inAppResponse);
cleverTapResponses.add(new MetadataResponse(config, deviceInfo, this));
@@ -168,12 +168,12 @@ public NetworkManager(
/**
* Flushes the events queue from the local database to CleverTap servers.
*
- * @param context The Context object.
- * @param eventGroup The EventGroup indicating the type of events to be flushed.
- * @param caller The optional caller identifier.
+ * @param context The Context object.
+ * @param eventGroup The EventGroup indicating the type of events to be flushed.
+ * @param caller The optional caller identifier.
*/
@Override
- public void flushDBQueue(final Context context, final EventGroup eventGroup,@Nullable final String caller) {
+ public void flushDBQueue(final Context context, final EventGroup eventGroup, @Nullable final String caller) {
config.getLogger()
.verbose(config.getAccountId(), "Somebody has invoked me to send the queue to CleverTap servers");
@@ -192,8 +192,7 @@ public void flushDBQueue(final Context context, final EventGroup eventGroup,@Nul
if (eventGroup == EventGroup.PUSH_NOTIFICATION_VIEWED) {
// Notify listener for push impression sent to the server
- if (previousCursor!=null && previousCursor.getData()!=null)
- {
+ if (previousCursor != null && previousCursor.getData() != null) {
try {
notifyListenersForPushImpressionSentToServer(previousCursor.getData());
} catch (Exception e) {
@@ -215,7 +214,7 @@ public void flushDBQueue(final Context context, final EventGroup eventGroup,@Nul
}
// Send the events queue to CleverTap servers
- loadMore = sendQueue(context, eventGroup, queue,caller);
+ loadMore = sendQueue(context, eventGroup, queue, caller);
if (!loadMore) {
// network error
controllerManager.invokeCallbacksForNetworkError();
@@ -274,19 +273,16 @@ public String getNewNamespaceARPKey() {
return "ARP:" + accountId + ":" + deviceInfo.getDeviceID();
}
- public void incrementResponseFailureCount() {
- responseFailureCount++;
- }
-
@Override
public void initHandshake(final EventGroup eventGroup, final Runnable handshakeSuccessCallback) {
+ // Always set this to 0 so that the handshake is not performed during a HTTP failure
responseFailureCount = 0;
performHandshakeForDomain(context, eventGroup, handshakeSuccessCallback);
}
@Override
public boolean needsHandshakeForDomain(final EventGroup eventGroup) {
- final String domain = getDomainFromPrefsOrMetadata(eventGroup);
+ final String domain = getDomain(eventGroup);
boolean needHandshakeDueToFailure = responseFailureCount > 5;
if (needHandshakeDueToFailure) {
setDomain(context, null);
@@ -310,106 +306,12 @@ public void setJ(Context context, long j) {
StorageHelper.persist(editor);
}
- HttpsURLConnection buildHttpsURLConnection(final String endpoint)
- throws IOException {
- URL url = new URL(endpoint);
- HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
- conn.setConnectTimeout(10000);
- conn.setReadTimeout(10000);
- conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
- conn.setRequestProperty("X-CleverTap-Account-ID", config.getAccountId());
- conn.setRequestProperty("X-CleverTap-Token", config.getAccountToken());
- conn.setInstanceFollowRedirects(false);
- if (config.isSslPinningEnabled()) {
- SSLContext _sslContext = getSSLContext();
- if (_sslContext != null) {
- conn.setSSLSocketFactory(getPinnedCertsSslSocketfactory(_sslContext));
- }
- }
- return conn;
- }
-
int getCurrentRequestTimestamp() {
- return currentRequestTimestamp;
- }
-
- void setCurrentRequestTimestamp(final int currentRequestTimestamp) {
- this.currentRequestTimestamp = currentRequestTimestamp;
- }
-
- String getDomain(boolean defaultToHandshakeURL, final EventGroup eventGroup) {
- String domain = getDomainFromPrefsOrMetadata(eventGroup);
-
- final boolean emptyDomain = domain == null || domain.trim().length() == 0;
- if (emptyDomain && !defaultToHandshakeURL) {
- return null;
- }
-
- if (emptyDomain) {
- domain = Constants.PRIMARY_DOMAIN + "/hello";
- } else if (eventGroup == EventGroup.VARIABLES) {
- domain += eventGroup.additionalPath;
- } else {
- domain += "/a1";
- }
-
- return domain;
- }
-
- public String getDomainFromPrefsOrMetadata(final EventGroup eventGroup) {
- try {
- // Always set this to 0 so that the handshake is not performed during a HTTP failure
- setResponseFailureCount(0);
-
- final String region = config.getAccountRegion();
- final String proxyDomain = config.getProxyDomain();
- final String spikyProxyDomain = config.getSpikyProxyDomain();
-
- if (region != null && region.trim().length() > 0) {
- return (eventGroup.equals(EventGroup.PUSH_NOTIFICATION_VIEWED)) ?
- region.trim().toLowerCase() + eventGroup.httpResource + "." + Constants.PRIMARY_DOMAIN :
- region.trim().toLowerCase() + "." + Constants.PRIMARY_DOMAIN;
- } else if (eventGroup.equals(EventGroup.REGULAR) && proxyDomain != null && proxyDomain.trim().length() > 0) {
- return proxyDomain;
- } else if (eventGroup.equals(EventGroup.PUSH_NOTIFICATION_VIEWED) && spikyProxyDomain != null && spikyProxyDomain.trim().length() > 0) {
- return spikyProxyDomain;
- }
- } catch (Throwable t) {
- // Ignore
- }
-
- return (eventGroup.equals(EventGroup.PUSH_NOTIFICATION_VIEWED)) ?
- StorageHelper.getStringFromPrefs(context, config, Constants.SPIKY_KEY_DOMAIN_NAME, null) :
- StorageHelper.getStringFromPrefs(context, config, Constants.KEY_DOMAIN_NAME, null);
+ return ctApi.getCurrentRequestTimestampSeconds();
}
- String getEndpoint(final boolean defaultToHandshakeURL, final EventGroup eventGroup) {
- String domain = getDomain(defaultToHandshakeURL, eventGroup);
- if (domain == null) {
- logger.verbose(config.getAccountId(), "Unable to configure endpoint, domain is null");
- return null;
- }
-
- final String accountId = config.getAccountId();
-
- if (accountId == null) {
- logger.verbose(config.getAccountId(), "Unable to configure endpoint, accountID is null");
- return null;
- }
-
- String endpoint = "https://" + domain + "?os=Android&t=" + deviceInfo.getSdkVersion();
- endpoint += "&z=" + accountId;
-
- final boolean needsHandshake = needsHandshakeForDomain(eventGroup);
- // Don't attach ts if its handshake
- if (needsHandshake) {
- return endpoint;
- }
-
- currentRequestTimestamp = (int) (System.currentTimeMillis() / 1000);
- endpoint += "&ts=" + getCurrentRequestTimestamp();
-
- return endpoint;
+ public String getDomain(final EventGroup eventGroup) {
+ return ctApi.getActualDomain(eventGroup == EventGroup.PUSH_NOTIFICATION_VIEWED);
}
int getFirstRequestTimestamp() {
@@ -424,27 +326,18 @@ void setLastRequestTimestamp(int ts) {
StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_LAST_TS), ts);
}
- int getResponseFailureCount() {
- return responseFailureCount;
- }
-
- void setResponseFailureCount(final int responseFailureCount) {
- this.responseFailureCount = responseFailureCount;
- }
-
boolean hasDomainChanged(final String newDomain) {
final String oldDomain = StorageHelper.getStringFromPrefs(context, config, Constants.KEY_DOMAIN_NAME, null);
return !newDomain.equals(oldDomain);
}
/**
- * Constructs a header JSON object and inserts it into a new JSON array along with the given JSON array.
+ * Constructs a header {@link JSONObject} to be included as a first element of a sendQueue request
*
* @param context The Context object.
* @param caller The optional caller identifier.
- * @return A new JSON array as a string with the constructed header and the given JSON array.
*/
- JSONObject getHeader(Context context, @Nullable final String caller) {//[{}]
+ private JSONObject getQueueHeader(Context context, @Nullable final String caller) {
try {
// Construct the header JSON object
final JSONObject header = new JSONObject();
@@ -468,7 +361,7 @@ JSONObject getHeader(Context context, @Nullable final String caller) {//[{}]
// Add app fields
JSONObject appFields = deviceInfo.getAppLaunchedFields();
- if(coreMetaData.isWebInterfaceInitializedExternally()) {
+ if (coreMetaData.isWebInterfaceInitializedExternally()) {
appFields.put("wv_init", true);
}
header.put("af", appFields);
@@ -488,9 +381,7 @@ JSONObject getHeader(Context context, @Nullable final String caller) {//[{}]
String token = config.getAccountToken();
if (accountId == null || token == null) {
- logger
- .debug(config.getAccountId(),
- "Account ID/token not found, unable to configure queue request");
+ logger.debug(config.getAccountId(), "Account ID/token not found, unable to configure queue request");
return null;
}
@@ -527,8 +418,9 @@ JSONObject getHeader(Context context, @Nullable final String caller) {//[{}]
header.put("frs", coreMetaData.isFirstRequestInSession());
// Add debug flag to show errors and events on the integration-debugger
- if(CleverTapAPI.getDebugLevel() == 3)
- header.put("debug",true);
+ if (CleverTapAPI.getDebugLevel() == 3) {
+ header.put("debug", true);
+ }
coreMetaData.setFirstRequestInSession(false);
@@ -538,8 +430,8 @@ JSONObject getHeader(Context context, @Nullable final String caller) {//[{}]
if (arp != null && arp.length() > 0) {
header.put("arp", arp);
}
- } catch (Throwable t) {
- logger.verbose(config.getAccountId(), "Failed to attach ARP", t);
+ } catch (JSONException e) {
+ logger.verbose(config.getAccountId(), "Failed to attach ARP", e);
}
// Add ref (Referrer Information)
@@ -565,8 +457,8 @@ JSONObject getHeader(Context context, @Nullable final String caller) {//[{}]
header.put("ref", ref);
}
- } catch (Throwable t) {
- logger.verbose(config.getAccountId(), "Failed to attach ref", t);
+ } catch (JSONException e) {
+ logger.verbose(config.getAccountId(), "Failed to attach ref", e);
}
// Add wzrk_ref (CleverTap-specific Parameters)
@@ -578,59 +470,38 @@ JSONObject getHeader(Context context, @Nullable final String caller) {//[{}]
// Attach InAppFC to header if available
if (controllerManager.getInAppFCManager() != null) {
Logger.v("Attaching InAppFC to Header");
- controllerManager.getInAppFCManager().attachToHeader(context, header);
+ header.put("imp", controllerManager.getInAppFCManager().getShownTodayCount());
+ header.put("tlc", controllerManager.getInAppFCManager().getInAppsCount(context));
} else {
logger.verbose(config.getAccountId(),
"controllerManager.getInAppFCManager() is NULL, not Attaching InAppFC to Header");
}
- // Create a new JSON array with the header and the given JSON array
- // Return the new JSON array as a string
- // Resort to string concat for backward compatibility
return header;
- } catch (Throwable t) {
- logger.verbose(config.getAccountId(), "CommsManager: Failed to attach header", t);
+ } catch (JSONException e) {
+ logger.verbose(config.getAccountId(), "CommsManager: Failed to attach header", e);
return null;
}
}
- void performHandshakeForDomain(final Context context, final EventGroup eventGroup,
- final Runnable handshakeSuccessCallback) {
- final String endpoint = getEndpoint(true, eventGroup);
- if (endpoint == null) {
- logger.verbose(config.getAccountId(), "Unable to perform handshake, endpoint is null");
- }
- logger.verbose(config.getAccountId(), "Performing handshake with " + endpoint);
+ private void performHandshakeForDomain(final Context context, final EventGroup eventGroup,
+ final Runnable handshakeSuccessCallback) {
- HttpsURLConnection conn = null;
- try {
- conn = buildHttpsURLConnection(endpoint);
- final int responseCode = conn.getResponseCode();
- if (responseCode != 200) {
- logger
- .verbose(config.getAccountId(),
- "Invalid HTTP status code received for handshake - " + responseCode);
- return;
- }
-
- logger.verbose(config.getAccountId(), "Received success from handshake :)");
+ try (Response response = ctApi.performHandshakeForDomain(eventGroup == EventGroup.PUSH_NOTIFICATION_VIEWED)) {
+ if (response.isSuccess()) {
+ logger.verbose(config.getAccountId(), "Received success from handshake :)");
- if (processIncomingHeaders(context, conn)) {
- logger.verbose(config.getAccountId(), "We are not muted");
- // We have a new domain, run the callback
- handshakeSuccessCallback.run();
- }
- } catch (Throwable t) {
- logger.verbose(config.getAccountId(), "Failed to perform handshake!", t);
- } finally {
- if (conn != null) {
- try {
- conn.getInputStream().close();
- conn.disconnect();
- } catch (Throwable t) {
- // Ignore
+ if (processIncomingHeaders(context, response)) {
+ logger.verbose(config.getAccountId(), "We are not muted");
+ // We have a new domain, run the callback
+ handshakeSuccessCallback.run();
}
+ } else {
+ logger.verbose(config.getAccountId(),
+ "Invalid HTTP status code received for handshake - " + response.getCode());
}
+ } catch (Exception e) {
+ logger.verbose(config.getAccountId(), "Failed to perform handshake!", e);
}
}
@@ -639,8 +510,8 @@ void performHandshakeForDomain(final Context context, final EventGroup eventGrou
*
* @return True to continue sending requests, false otherwise.
*/
- boolean processIncomingHeaders(final Context context, final HttpsURLConnection conn) {
- final String muteCommand = conn.getHeaderField(Constants.HEADER_MUTE);
+ private boolean processIncomingHeaders(final Context context, Response response) {
+ final String muteCommand = response.getHeaderValue(Constants.HEADER_MUTE);
if (muteCommand != null && muteCommand.trim().length() > 0) {
if (muteCommand.equals("true")) {
setMuted(context, true);
@@ -650,13 +521,13 @@ boolean processIncomingHeaders(final Context context, final HttpsURLConnection c
}
}
- final String domainName = conn.getHeaderField(Constants.HEADER_DOMAIN_NAME);
+ final String domainName = response.getHeaderValue(Constants.HEADER_DOMAIN_NAME);
Logger.v("Getting domain from header - " + domainName);
if (domainName == null || domainName.trim().length() == 0) {
return true;
}
- final String spikyDomainName = conn.getHeaderField(Constants.SPIKY_HEADER_DOMAIN_NAME);
+ final String spikyDomainName = response.getHeaderValue(Constants.SPIKY_HEADER_DOMAIN_NAME);
Logger.v("Getting spiky domain from header - " + spikyDomainName);
setMuted(context, false);
@@ -680,7 +551,8 @@ boolean processIncomingHeaders(final Context context, final HttpsURLConnection c
* @return True if the queue was sent successfully, false otherwise.
*/
@Override
- public boolean sendQueue(final Context context, final EventGroup eventGroup, final JSONArray queue, @Nullable final String caller) {
+ public boolean sendQueue(final Context context, final EventGroup eventGroup, final JSONArray queue,
+ @Nullable final String caller) {
if (queue == null || queue.length() <= 0) {
// Empty queue, no need to send
return false;
@@ -691,169 +563,160 @@ public boolean sendQueue(final Context context, final EventGroup eventGroup, fin
return false;
}
- HttpsURLConnection conn = null;
- try {
- final String endpoint = getEndpoint(false, eventGroup);
+ EndpointId endpointId = EndpointId.fromEventGroup(eventGroup);
+ JSONObject queueHeader = getQueueHeader(context, caller);
+ applyQueueHeaderListeners(queueHeader, endpointId);
- // This is just a safety check, which would only arise
- // if upstream didn't adhere to the protocol (sent nothing during the initial handshake)
- if (endpoint == null) {
- logger.debug(config.getAccountId(), "Problem configuring queue endpoint, unable to send queue");
- return false;
- }
-
- conn = buildHttpsURLConnection(endpoint);
+ final SendQueueRequestBody body = new SendQueueRequestBody(queueHeader, queue);
+ logger.debug(config.getAccountId(), "Send queue contains " + queue.length() + " items: " + body);
- final String body;
-// final String req = insertHeader(context, queue,caller);
- final JSONObject header = getHeader(context, caller);
- final EndpointId endpointId = EndpointId.fromString(endpoint);
- String req;
- if (header == null) {
- req = queue.toString();
+ try (Response response = callApiForEventGroup(eventGroup, body)) {
+ networkRetryCount = 0;
+ boolean isProcessed;
+ if (eventGroup == EventGroup.VARIABLES) {
+ isProcessed = handleVariablesResponse(response);
} else {
- for (NetworkHeadersListener listener : mNetworkHeadersListeners) {
- final JSONObject headersToAttach = listener.onAttachHeaders(endpointId);
- if (headersToAttach != null) {
- CTXtensions.copyFrom(header, headersToAttach);
- }
- }
- req = "[" + header + ", " + queue.toString().substring(1);
- }
-
- if (req == null) {
- logger.debug(config.getAccountId(), "Problem configuring queue request, unable to send queue");
- return false;
+ isProcessed = handleSendQueueResponse(response, body, endpointId);
}
- logger.debug(config.getAccountId(), "Send queue contains " + queue.length() + " items: " + req);
- logger.debug(config.getAccountId(), "Sending queue to: " + endpoint);
-
- // Enable output for writing data
- conn.setDoOutput(true);
- // Write the request body to the connection output stream
- conn.getOutputStream().write(req.getBytes("UTF-8"));
-
- // Get the HTTP response code
- final int responseCode = conn.getResponseCode();
-
- if (eventGroup == EventGroup.VARIABLES) {
- if (handleVariablesResponseError(responseCode, conn)) {
- return false;
- }
+ if (isProcessed) {
+ responseFailureCount = 0;
} else {
- // Always check for a 200 OK
- if (responseCode != 200) {
- throw new IOException("Response code is not 200. It is " + responseCode);
- }
+ responseFailureCount++;
}
-
- // Check for a change in domain
- final String newDomain = conn.getHeaderField(Constants.HEADER_DOMAIN_NAME);
- if (newDomain != null && newDomain.trim().length() > 0) {
- if (hasDomainChanged(newDomain)) {
- // The domain has changed. Return a status of -1 so that the caller retries
- setDomain(context, newDomain);
- logger.debug(config.getAccountId(),
- "The domain has changed to " + newDomain + ". The request will be retried shortly.");
- return false;
- }
+ return isProcessed;
+ } catch (Exception e) {
+ networkRetryCount++;
+ responseFailureCount++;
+ logger.debug(config.getAccountId(), "An exception occurred while sending the queue, will retry: ", e);
+ if (callbackManager.getFailureFlushListener() != null) {
+ callbackManager.getFailureFlushListener().failureFlush(context);
}
+ return false;
+ }
+ }
+ private void applyQueueHeaderListeners(JSONObject queueHeader, EndpointId endpointId) {
+ if (queueHeader != null) {
for (NetworkHeadersListener listener : mNetworkHeadersListeners) {
- if (header != null) {
- listener.onSentHeaders(header, endpointId);
+ final JSONObject headersToAttach = listener.onAttachHeaders(endpointId);
+ if (headersToAttach != null) {
+ CTXtensions.copyFrom(queueHeader, headersToAttach);
}
}
+ }
+ }
- if (processIncomingHeaders(context, conn)) {
- // Read the response body from the connection input stream
- BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
+ private Response callApiForEventGroup(EventGroup eventGroup, SendQueueRequestBody body) {
+ if (eventGroup == EventGroup.VARIABLES) {
+ return ctApi.defineVars(body);
+ } else {
+ return ctApi.sendQueue(eventGroup == EventGroup.PUSH_NOTIFICATION_VIEWED, body);
+ }
+ }
- StringBuilder sb = new StringBuilder();
- String line;
- while ((line = br.readLine()) != null) {
- sb.append(line);
- }
- body = sb.toString();
+ private boolean handleVariablesResponse(@NonNull Response response) {
+ if (response.isSuccess()) {
+ String bodyString = response.readBody();
+ JSONObject bodyJson = CTXtensions.toJsonOrNull(bodyString);
- // Process the response body
- if (eventGroup == EventGroup.VARIABLES) {
- processVariablesResponse(body);
+ logger.verbose(config.getAccountId(), "Processing variables response : " + bodyJson);
+
+ new ARPResponse(config, this, validator, controllerManager)
+ .processResponse(bodyJson, bodyString, this.context);
+ new SyncUpstreamResponse(localDataStore, logger, config.getAccountId())
+ .processResponse(bodyJson, bodyString, context);
+ return true;
+ } else {
+ handleVariablesResponseError(response);
+ return false;
+ }
+ }
+
+ private void handleVariablesResponseError(Response response) {
+ switch (response.getCode()) {
+ case 400:
+ JSONObject errorStreamJson = CTXtensions.toJsonOrNull(response.readBody());
+ if (errorStreamJson != null && !TextUtils.isEmpty(errorStreamJson.optString("error"))) {
+ String errorMessage = errorStreamJson.optString("error");
+ logger.info("variables", "Error while syncing vars: " + errorMessage);
} else {
- // check if there is app launched/wzrk_fetch event
- boolean found = false;
- for (int index = 0; index < queue.length(); index++) {
- JSONObject event = queue.getJSONObject(index);
- final String eventType = event.getString("type");
- if ("event".equals(eventType)) {
- final String evtName = event.getString("evtName");
- if (Constants.APP_LAUNCHED_EVENT.equals(evtName) || Constants.WZRK_FETCH.equals(evtName)) {
- found = true;
- }
- }
- }
- processAllResponses(body, found);
+ logger.info("variables", "Error while syncing vars.");
}
- }
+ return;
+ case 401:
+ logger.info("variables", "Unauthorized access from a non-test profile. "
+ + "Please mark this profile as a test profile from the CleverTap dashboard.");
+ return;
+ default:
+ logger.info("variables", "Response code " + response.getCode() + " while syncing vars.");
+ }
+ }
- setLastRequestTimestamp(getCurrentRequestTimestamp());
- setFirstRequestTimestampIfNeeded(getCurrentRequestTimestamp());
+ private boolean handleSendQueueResponse(@NonNull Response response, SendQueueRequestBody body,
+ EndpointId endpointId) {
+ if (!response.isSuccess()) {
+ handleSendQueueResponseError(response);
+ return false;
+ }
- logger.debug(config.getAccountId(), "Queue sent successfully");
+ String newDomain = response.getHeaderValue(Constants.HEADER_DOMAIN_NAME);
- responseFailureCount = 0;
- networkRetryCount = 0; //reset retry count when queue is sent successfully
- return true;
- } catch (Throwable e) {
+ if (newDomain != null && !newDomain.trim().isEmpty() && hasDomainChanged(newDomain)) {
+ setDomain(context, newDomain);
logger.debug(config.getAccountId(),
- "An exception occurred while sending the queue, will retry: ", e);
- responseFailureCount++;
- networkRetryCount++;
- callbackManager.getFailureFlushListener().failureFlush(context);
+ "The domain has changed to " + newDomain + ". The request will be retried shortly.");
return false;
- } finally {
- if (conn != null) {
- try {
- conn.getInputStream().close();
- conn.disconnect();
- } catch (Throwable t) {
- // Ignore
- }
+ }
+
+ if (body.getQueueHeader() != null) {
+ for (NetworkHeadersListener listener : mNetworkHeadersListeners) {
+ listener.onSentHeaders(body.getQueueHeader(), endpointId);
}
}
- }
- private void processVariablesResponse(String body) {
- try {
- JSONObject jsonObject = new JSONObject(body);
+ if (!processIncomingHeaders(context, response)) {
+ return false;
+ }
- logger.verbose(config.getAccountId(), "Processing variables response : " + jsonObject);
+ logger.debug(config.getAccountId(), "Queue sent successfully");
+ setLastRequestTimestamp(getCurrentRequestTimestamp());
+ setFirstRequestTimestampIfNeeded(getCurrentRequestTimestamp());
- new ARPResponse(config, this, validator, controllerManager)
- .processResponse(jsonObject, body, this.context);
- new SyncUpstreamResponse(localDataStore, logger, config.getAccountId())
- .processResponse(jsonObject, body, context);
- } catch (JSONException e) {
- logger.verbose(config.getAccountId(), "Error in parsing response.", e);
- incrementResponseFailureCount();
+ String bodyString = response.readBody();
+ JSONObject bodyJson = CTXtensions.toJsonOrNull(bodyString);
+ logger.verbose(config.getAccountId(), "Processing response : " + bodyJson);
+
+ boolean isFullResponse = doesBodyContainAppLaunchedOrFetchEvents(body);
+ for (CleverTapResponse processor : cleverTapResponses) {
+ processor.isFullResponse = isFullResponse;
+ processor.processResponse(bodyJson, bodyString, context);
}
- }
- private void processAllResponses(String body, boolean isFullResponse) {
- try {
- JSONObject jsonObject = new JSONObject(body);
+ return true;
+ }
- logger.verbose(config.getAccountId(), "Processing response : " + jsonObject);
+ private void handleSendQueueResponseError(@NonNull Response response) {
+ logger.info("Received error response code: " + response.getCode());
+ }
- for (CleverTapResponse response: cleverTapResponses) {
- response.isFullResponse = isFullResponse;
- response.processResponse(jsonObject, body, context);
+ private boolean doesBodyContainAppLaunchedOrFetchEvents(SendQueueRequestBody body) {
+ // check if there is app launched/wzrk_fetch event
+ for (int index = 0; index < body.getQueue().length(); index++) {
+ try {
+ JSONObject event = body.getQueue().getJSONObject(index);
+ final String eventType = event.getString("type");
+ if ("event".equals(eventType)) {
+ final String evtName = event.getString("evtName");
+ if (Constants.APP_LAUNCHED_EVENT.equals(evtName) || Constants.WZRK_FETCH.equals(evtName)) {
+ return true;
+ }
+ }
+ } catch (JSONException jsonException) {
+ //skip
}
- } catch (JSONException e) {
- logger.verbose(config.getAccountId(), "Error in parsing response.", e);
- incrementResponseFailureCount();
}
+ return false;
}
private void notifyListenersForPushImpressionSentToServer(final JSONArray queue) throws JSONException {
@@ -897,55 +760,11 @@ private void notifyListenerForPushImpressionSentToServer(@NonNull String listene
}
}
- private boolean handleVariablesResponseError(int responseCode, HttpsURLConnection conn) {
- switch (responseCode) {
- case 200:
- logger.info("variables", "Vars synced successfully.");
- return false;
-
- case 400:
- JSONObject errorStreamJson = getErrorStreamAsJson(conn);
- if (errorStreamJson != null && !TextUtils.isEmpty(errorStreamJson.optString("error"))) {
- String errorMessage = errorStreamJson.optString("error");
- logger.info("variables", "Error while syncing vars: " + errorMessage);
- } else {
- logger.info("variables", "Error while syncing vars.");
- }
- return true;
-
- case 401:
- logger.info("variables", "Unauthorized access from a non-test profile. "
- + "Please mark this profile as a test profile from the CleverTap dashboard.");
- return true;
-
- default:
- logger.info("variables", "Response code " + responseCode + " while syncing vars.");
- return true;
- }
- }
-
- private JSONObject getErrorStreamAsJson(HttpsURLConnection conn) {
- try {
- BufferedReader br = new BufferedReader(
- new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
-
- StringBuilder text = new StringBuilder();
- String line;
- while ((line = br.readLine()) != null) {
- text.append(line);
- }
-
- return new JSONObject(text.toString());
-
- } catch (IOException | JSONException e) {
- return null;
- }
- }
-
- void setDomain(final Context context, String domainName) {
+ private void setDomain(final Context context, String domainName) {
logger.verbose(config.getAccountId(), "Setting domain to " + domainName);
StorageHelper.putString(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_DOMAIN_NAME),
domainName);
+ ctApi.setDomain(domainName);
if (callbackManager.getSCDomainListener() != null) {
if (domainName != null) {
@@ -956,17 +775,18 @@ void setDomain(final Context context, String domainName) {
}
}
- void setFirstRequestTimestampIfNeeded(int ts) {
+ private void setFirstRequestTimestampIfNeeded(int ts) {
if (getFirstRequestTimestamp() > 0) {
return;
}
StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_FIRST_TS), ts);
}
- void setSpikyDomain(final Context context, String spikyDomainName) {
+ private void setSpikyDomain(final Context context, String spikyDomainName) {
logger.verbose(config.getAccountId(), "Setting spiky domain to " + spikyDomainName);
StorageHelper.putString(context, StorageHelper.storageKeyWithSuffix(config, Constants.SPIKY_KEY_DOMAIN_NAME),
spikyDomainName);
+ ctApi.setSpikyDomain(spikyDomainName);
}
/**
@@ -1009,8 +829,8 @@ private JSONObject getARP() {
logger.verbose(config.getAccountId(),
"Fetched ARP for namespace key: " + nameSpaceKey + " values: " + all);
return ret;
- } catch (Throwable t) {
- logger.verbose(config.getAccountId(), "Failed to construct ARP object", t);
+ } catch (Exception e) {
+ logger.verbose(config.getAccountId(), "Failed to construct ARP object", e);
return null;
}
}
@@ -1061,7 +881,7 @@ private SharedPreferences migrateARPToNewNameSpace(String newKey, String oldKey)
"ARP update for key " + kv.getKey() + " rejected (invalid data type)");
}
}
- logger.verbose(config.getAccountId(), "Completed ARP update for namespace key: " + newKey + "");
+ logger.verbose(config.getAccountId(), "Completed ARP update for namespace key: " + newKey);
StorageHelper.persist(editor);
oldPrefs.edit().clear().apply();
return newPrefs;
@@ -1083,27 +903,4 @@ private void setMuted(final Context context, boolean mute) {
StorageHelper.putInt(context, StorageHelper.storageKeyWithSuffix(config, Constants.KEY_MUTED), 0);
}
}
-
- private static SSLSocketFactory getPinnedCertsSslSocketfactory(SSLContext sslContext) {
- if (sslContext == null) {
- return null;
- }
-
- if (sslSocketFactory == null) {
- try {
- sslSocketFactory = sslContext.getSocketFactory();
- Logger.d("Pinning SSL session to DigiCertGlobalRoot CA certificate");
- } catch (Throwable e) {
- Logger.d("Issue in pinning SSL,", e);
- }
- }
- return sslSocketFactory;
- }
-
- private static synchronized SSLContext getSSLContext() {
- if (sslContext == null) {
- sslContext = new SSLContextBuilder().build();
- }
- return sslContext;
- }
}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/SSLContextBuilder.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/SSLContextBuilder.java
deleted file mode 100644
index 42a957fd9..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/SSLContextBuilder.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.clevertap.android.sdk.network;
-
-
-import com.clevertap.android.sdk.Logger;
-import java.io.BufferedInputStream;
-import java.io.InputStream;
-import java.security.KeyStore;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManagerFactory;
-
-final class SSLContextBuilder {
-
- SSLContext build() {
- try {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
- keyStore.load(null, null);//Use null inputstream & password to create empty key store
-
- //noinspection ConstantConditions
- InputStream inputStream3 = new BufferedInputStream(getClass().getClassLoader().getResourceAsStream("com/clevertap/android/sdk/certificates/AmazonRootCA1.cer"));
- X509Certificate x509Certificate3 = (X509Certificate) certificateFactory.generateCertificate(inputStream3);
- keyStore.setCertificateEntry("AmazonRootCA1", x509Certificate3);
-
- trustManagerFactory.init(keyStore);
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
- Logger.d("SSL Context built");
- return sslContext;
- } catch (Throwable e) {
- Logger.i("Error building SSL Context", e);
- }
- return null;
- }
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApi.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApi.kt
new file mode 100644
index 000000000..db2cdc0ff
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApi.kt
@@ -0,0 +1,116 @@
+package com.clevertap.android.sdk.network.api
+
+import android.net.Uri
+import com.clevertap.android.sdk.Logger
+import com.clevertap.android.sdk.network.http.CtHttpClient
+import com.clevertap.android.sdk.network.http.Request
+import com.clevertap.android.sdk.network.http.Response
+
+class CtApi(
+ private val httpClient: CtHttpClient,
+ val defaultDomain: String,
+ var domain: String?,
+ var spikyDomain: String?,
+ var region: String?,
+ var proxyDomain: String?,
+ var spikyProxyDomain: String?,
+ accountId: String,
+ accountToken: String,
+ sdkVersion: String,
+ private val logger: Logger,
+ private val logTag: String
+) {
+
+ companion object {
+
+ const val DEFAULT_CONTENT_TYPE = "application/json; charset=utf-8"
+ const val DEFAULT_QUERY_PARAM_OS = "Android"
+ }
+
+ private val defaultHeaders: Map = mapOf(
+ "Content-Type" to DEFAULT_CONTENT_TYPE,
+ "X-CleverTap-Account-ID" to accountId,
+ "X-CleverTap-Token" to accountToken
+ )
+ private val defaultQueryParams: Map = mapOf(
+ "os" to DEFAULT_QUERY_PARAM_OS,
+ "t" to sdkVersion,
+ "z" to accountId
+ )
+ private val spikyRegionSuffix = "-spiky"
+ var currentRequestTimestampSeconds = 0
+ private set
+
+ fun sendQueue(useSpikyDomain: Boolean, body: SendQueueRequestBody): Response =
+ httpClient.execute(createRequest("a1", body.toString(), useSpikyDomain, includeTs = true))
+
+ fun performHandshakeForDomain(useSpikyDomain: Boolean): Response {
+ val request = createRequest("hello", null, useSpikyDomain, includeTs = false)
+ logger.verbose(logTag, "Performing handshake with ${request.url}")
+ return httpClient.execute(request)
+ }
+
+ fun defineVars(body: SendQueueRequestBody): Response =
+ httpClient.execute(
+ createRequest("defineVars", body.toString(), useSpikyDomain = false, includeTs = true)
+ )
+
+ fun getActualDomain(useSpikyDomain: Boolean): String? {
+ return when {
+ !region.isNullOrBlank() -> {
+ val regionSuffix = if (useSpikyDomain) spikyRegionSuffix else ""
+ "$region${regionSuffix}.$defaultDomain"
+ }
+
+ !useSpikyDomain && !proxyDomain.isNullOrBlank() -> {
+ proxyDomain
+ }
+
+ useSpikyDomain && !spikyProxyDomain.isNullOrBlank() -> {
+ spikyProxyDomain
+ }
+
+ else -> if (useSpikyDomain) {
+ spikyDomain
+ } else {
+ domain
+ }
+ }
+ }
+
+ private fun createRequest(
+ relativePath: String,
+ body: String?,
+ useSpikyDomain: Boolean,
+ includeTs: Boolean
+ ) = Request(
+ url = getUriForPath(path = relativePath, useSpikyDomain = useSpikyDomain, includeTs = includeTs),
+ headers = defaultHeaders,
+ body = body
+ )
+
+ private fun getUriForPath(path: String, useSpikyDomain: Boolean, includeTs: Boolean): Uri {
+ val builder = Uri.Builder()
+ .scheme("https")
+ .authority(getActualDomain(useSpikyDomain))
+ .appendPath(path)
+ .appendDefaultQueryParams()
+ if (includeTs) {
+ builder.appendTsQueryParam()
+ }
+ return builder.build()
+ }
+
+ private fun Uri.Builder.appendDefaultQueryParams(): Uri.Builder {
+ for (queryParam in defaultQueryParams) {
+ appendQueryParameter(queryParam.key, queryParam.value)
+ }
+
+ return this
+ }
+
+ private fun Uri.Builder.appendTsQueryParam(): Uri.Builder {
+ currentRequestTimestampSeconds = (System.currentTimeMillis() / 1000).toInt()
+ return appendQueryParameter("ts", currentRequestTimestampSeconds.toString())
+ }
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApiProvider.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApiProvider.kt
new file mode 100644
index 000000000..cdee50596
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/CtApiProvider.kt
@@ -0,0 +1,35 @@
+package com.clevertap.android.sdk.network.api
+
+import android.content.Context
+import com.clevertap.android.sdk.CleverTapInstanceConfig
+import com.clevertap.android.sdk.Constants
+import com.clevertap.android.sdk.DeviceInfo
+import com.clevertap.android.sdk.StorageHelper
+import com.clevertap.android.sdk.network.http.UrlConnectionHttpClient
+
+fun provideDefaultTestCtApi(
+ context: Context,
+ config: CleverTapInstanceConfig,
+ deviceInfo: DeviceInfo
+): CtApi {
+ val httpClient = UrlConnectionHttpClient(
+ isSslPinningEnabled = config.isSslPinningEnabled,
+ logger = config.logger,
+ logTag = config.accountId
+ )
+
+ return CtApi(
+ httpClient = httpClient,
+ defaultDomain = Constants.PRIMARY_DOMAIN,
+ domain = StorageHelper.getStringFromPrefs(context, config, Constants.KEY_DOMAIN_NAME, null),
+ spikyDomain = StorageHelper.getStringFromPrefs(context, config, Constants.SPIKY_KEY_DOMAIN_NAME, null),
+ region = config.accountRegion,
+ proxyDomain = config.proxyDomain,
+ spikyProxyDomain = config.spikyProxyDomain,
+ accountId = config.accountId,
+ accountToken = config.accountToken,
+ sdkVersion = deviceInfo.sdkVersion.toString(),
+ logger = config.logger,
+ logTag = config.accountId
+ )
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/SendQueueRequestBody.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/SendQueueRequestBody.kt
new file mode 100644
index 000000000..c0e5934dc
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/api/SendQueueRequestBody.kt
@@ -0,0 +1,14 @@
+package com.clevertap.android.sdk.network.api
+
+import org.json.JSONArray
+import org.json.JSONObject
+
+class SendQueueRequestBody(var queueHeader: JSONObject?, var queue: JSONArray) {
+
+ override fun toString(): String = if (queueHeader == null) {
+ queue.toString()
+ } else {
+ // prepend header to the queue array
+ "[${queueHeader.toString()},${queue.toString().substring(1)}"
+ }
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/CtHttpClient.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/CtHttpClient.kt
new file mode 100644
index 000000000..3836e0d3f
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/CtHttpClient.kt
@@ -0,0 +1,6 @@
+package com.clevertap.android.sdk.network.http
+
+fun interface CtHttpClient {
+
+ fun execute(request: Request): Response
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Request.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Request.kt
new file mode 100644
index 000000000..36356321f
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Request.kt
@@ -0,0 +1,5 @@
+package com.clevertap.android.sdk.network.http
+
+import android.net.Uri
+
+class Request(val url: Uri, val headers: Map, val body: String?)
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Response.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Response.kt
new file mode 100644
index 000000000..ab37a1cde
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/Response.kt
@@ -0,0 +1,32 @@
+package com.clevertap.android.sdk.network.http
+
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.Closeable
+import java.io.InputStream
+import java.io.Reader
+import java.net.HttpURLConnection
+
+class Response(
+ val request: Request,
+ val code: Int,
+ val headers: Map>,
+ bodyStream: InputStream?,
+ private val closeDelegate: () -> Unit
+) : Closeable {
+
+ private val bodyReader: Reader? = bodyStream?.bufferedReader(Charsets.UTF_8)
+
+ fun isSuccess(): Boolean = code == HttpURLConnection.HTTP_OK
+
+ fun getHeaderValue(header: String): String? = headers[header]?.lastOrNull()
+
+ fun readBody(): String? {
+ return bodyReader?.readText()
+ }
+
+ override fun close() {
+ bodyReader?.close()
+ closeDelegate()
+ }
+}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/UrlConnectionHttpClient.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/UrlConnectionHttpClient.kt
new file mode 100644
index 000000000..32730d915
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/http/UrlConnectionHttpClient.kt
@@ -0,0 +1,101 @@
+package com.clevertap.android.sdk.network.http
+
+import com.clevertap.android.sdk.Logger
+import java.io.BufferedInputStream
+import java.io.InputStream
+import java.net.HttpURLConnection
+import java.net.URL
+import java.security.KeyStore
+import java.security.cert.CertificateFactory
+import java.security.cert.X509Certificate
+import javax.net.ssl.HttpsURLConnection
+import javax.net.ssl.SSLContext
+import javax.net.ssl.SSLSocketFactory
+import javax.net.ssl.TrustManagerFactory
+
+class UrlConnectionHttpClient(
+ var isSslPinningEnabled: Boolean,
+ private val logger: Logger,
+ private val logTag: String
+) : CtHttpClient {
+
+ var readTimeout = 10000
+ var connectTimeout = 10000
+
+ private val sslSocketFactory: SSLSocketFactory? by lazy {
+ try {
+ Logger.d("Pinning SSL session to DigiCertGlobalRoot CA certificate")
+ sslContext?.socketFactory
+ } catch (e: Exception) {
+ Logger.d("Issue in pinning SSL,", e)
+ null
+ }
+ }
+ private val sslContext: SSLContext? by lazy { createSslContext() }
+
+ override fun execute(request: Request): Response {
+ var connection: HttpsURLConnection? = null
+
+ try {
+ connection = openHttpsURLConnection(request)
+
+ if (request.body != null) {
+ connection.doOutput = true
+ connection.outputStream.use {
+ it.write(request.body.toByteArray(Charsets.UTF_8))
+ }
+ }
+ logger.debug(logTag, "Sending request to: ${request.url}")
+
+ // execute request
+ val responseCode = connection.responseCode
+ val headers = connection.headerFields
+ val disconnectConnection = { connection.disconnect() }
+
+ return if (responseCode == HttpURLConnection.HTTP_OK) {
+ Response(request, responseCode, headers, connection.inputStream, disconnectConnection)
+ } else {
+ Response(request, responseCode, headers, connection.errorStream, disconnectConnection)
+ }
+ } catch (e: Exception) {
+ connection?.disconnect()
+ throw e
+ }
+ }
+
+ private fun openHttpsURLConnection(request: Request): HttpsURLConnection {
+ val url = URL(request.url.toString())
+ val connection = url.openConnection() as HttpsURLConnection
+ connection.connectTimeout = connectTimeout
+ connection.readTimeout = readTimeout
+ for (header in request.headers) {
+ connection.setRequestProperty(header.key, header.value)
+ }
+ connection.instanceFollowRedirects = false
+ if (isSslPinningEnabled && sslContext != null) {
+ connection.sslSocketFactory = sslSocketFactory
+ }
+ return connection
+ }
+
+ private fun createSslContext(): SSLContext? {
+ try {
+ val certificateFactory = CertificateFactory.getInstance("X.509")
+ val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
+ val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
+ keyStore.load(null, null) //Use null InputStream & password to create empty key store
+ val inputStream: InputStream =
+ BufferedInputStream(javaClass.classLoader?.getResourceAsStream("com/clevertap/android/sdk/certificates/AmazonRootCA1.cer"))
+ val x509Certificate3 = certificateFactory.generateCertificate(inputStream) as X509Certificate
+ keyStore.setCertificateEntry("AmazonRootCA1", x509Certificate3)
+ trustManagerFactory.init(keyStore)
+ val sslContext = SSLContext.getInstance("TLS")
+ sslContext.init(null, trustManagerFactory.trustManagers, null)
+ Logger.d("SSL Context built")
+ return sslContext
+ } catch (e: Exception) {
+ Logger.i("Error building SSL Context", e)
+ }
+ return null
+ }
+}
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/LocalDataStoreProvider.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/LocalDataStoreProvider.kt
new file mode 100644
index 000000000..40fe3d2df
--- /dev/null
+++ b/clevertap-core/src/test/java/com/clevertap/android/sdk/LocalDataStoreProvider.kt
@@ -0,0 +1,15 @@
+package com.clevertap.android.sdk
+
+import android.content.Context
+import com.clevertap.android.sdk.cryption.CryptHandler
+
+object LocalDataStoreProvider {
+
+ fun provideLocalDataStore(
+ context: Context,
+ config: CleverTapInstanceConfig,
+ cryptHandler: CryptHandler
+ ): LocalDataStore {
+ return LocalDataStore(context, config, cryptHandler)
+ }
+}
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt
new file mode 100644
index 000000000..4dd3293fd
--- /dev/null
+++ b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt
@@ -0,0 +1,154 @@
+package com.clevertap.android.sdk.network
+
+import com.clevertap.android.sdk.CTLockManager
+import com.clevertap.android.sdk.CallbackManager
+import com.clevertap.android.sdk.Constants
+import com.clevertap.android.sdk.ControllerManager
+import com.clevertap.android.sdk.CoreMetaData
+import com.clevertap.android.sdk.LocalDataStoreProvider
+import com.clevertap.android.sdk.MockCoreState
+import com.clevertap.android.sdk.MockDeviceInfo
+import com.clevertap.android.sdk.cryption.CryptHandler
+import com.clevertap.android.sdk.cryption.CryptHandler.EncryptionAlgorithm.AES
+import com.clevertap.android.sdk.db.DBManager
+import com.clevertap.android.sdk.events.EventGroup.PUSH_NOTIFICATION_VIEWED
+import com.clevertap.android.sdk.events.EventGroup.REGULAR
+import com.clevertap.android.sdk.events.EventGroup.VARIABLES
+import com.clevertap.android.sdk.network.api.CtApi
+import com.clevertap.android.sdk.network.api.CtApiTestProvider
+import com.clevertap.android.sdk.network.http.MockHttpClient
+import com.clevertap.android.sdk.response.InAppResponse
+import com.clevertap.android.sdk.validation.ValidationResultStack
+import com.clevertap.android.sdk.validation.Validator
+import com.clevertap.android.shared.test.BaseTestCase
+import org.json.JSONObject
+import org.junit.*
+import org.junit.runner.*
+import org.mockito.*
+import org.robolectric.RobolectricTestRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(RobolectricTestRunner::class)
+class NetworkManagerTest : BaseTestCase() {
+
+ private lateinit var networkManager: NetworkManager
+ private lateinit var ctApi: CtApi
+ private lateinit var mockHttpClient: MockHttpClient
+
+ @Before
+ fun setUpNetworkManager() {
+ mockHttpClient = MockHttpClient()
+ ctApi = CtApiTestProvider.provideTestCtApiForConfig(cleverTapInstanceConfig, mockHttpClient)
+ networkManager = provideNetworkManager()
+ }
+
+ @Test
+ fun test_initHandshake_noHeaders_callSuccessCallback() {
+ val callback = Mockito.mock(Runnable::class.java)
+ networkManager.initHandshake(REGULAR, callback)
+ Mockito.verify(callback).run()
+ }
+
+ @Test
+ fun test_initHandshake_muteHeadersTrue_neverCallSuccessCallback() {
+ val callback = Mockito.mock(Runnable::class.java)
+ mockHttpClient.responseHeaders = mapOf(Constants.HEADER_MUTE to listOf("true"))
+ networkManager.initHandshake(REGULAR, callback)
+ Mockito.verify(callback, Mockito.never()).run()
+ }
+
+ @Test
+ fun test_initHandshake_muteHeadersFalse_callSuccessCallback() {
+ val callback = Mockito.mock(Runnable::class.java)
+ mockHttpClient.responseHeaders = mapOf(Constants.HEADER_MUTE to listOf("false"))
+ networkManager.initHandshake(REGULAR, callback)
+ Mockito.verify(callback).run()
+ }
+
+ @Test
+ fun test_initHandshake_changeDomainsHeaders_callSuccessCallbackAndUseDomains() {
+ val callback = Mockito.mock(Runnable::class.java)
+ val domain = "region.header-domain.com"
+ val spikyDomain = "region-spiky.header-domain.com"
+ // we only use changed domain when region is not configured
+ ctApi.region = null
+ mockHttpClient.responseHeaders = mapOf(
+ Constants.HEADER_DOMAIN_NAME to listOf(domain),
+ Constants.SPIKY_HEADER_DOMAIN_NAME to listOf(spikyDomain)
+ )
+ networkManager.initHandshake(REGULAR, callback)
+
+ Mockito.verify(callback).run()
+ assertEquals(domain, networkManager.getDomain(REGULAR))
+ assertEquals(spikyDomain, networkManager.getDomain(PUSH_NOTIFICATION_VIEWED))
+ }
+
+ @Test
+ fun test_sendQueue_requestFailure_returnFalse() {
+ mockHttpClient.alwaysThrowOnExecute = true
+ assertFalse(networkManager.sendQueue(appCtx, REGULAR, getSampleJsonArrayOfJsonObjects(2), null))
+ }
+
+ @Test
+ fun test_sendQueue_successResponseEmptyJsonBody_returnTrue() {
+ mockHttpClient.responseBody = JSONObject().toString()
+ assertTrue(networkManager.sendQueue(appCtx, REGULAR, getSampleJsonArrayOfJsonObjects(2), null))
+ }
+
+ @Test
+ fun test_sendQueue_successResponseNullBody_returnFalse() {
+ mockHttpClient.responseBody = null
+ assertFalse(networkManager.sendQueue(appCtx, REGULAR, getSampleJsonArrayOfJsonObjects(4), null))
+ }
+
+ @Test
+ fun test_sendVariablesQueue_successResponse_returnTrue() {
+ mockHttpClient.responseBody = JSONObject().toString()
+ assertTrue(networkManager.sendQueue(appCtx, VARIABLES, getSampleJsonArrayOfJsonObjects(1), null))
+ }
+
+ @Test
+ fun test_sendVariablesQueue_errorResponses_returnFalse() {
+ mockHttpClient.responseCode = 400
+ assertFalse(networkManager.sendQueue(appCtx, VARIABLES, getSampleJsonArrayOfJsonObjects(1), null))
+ mockHttpClient.responseCode = 401
+ assertFalse(networkManager.sendQueue(appCtx, VARIABLES, getSampleJsonArrayOfJsonObjects(1), null))
+ mockHttpClient.responseCode = 500
+ assertFalse(networkManager.sendQueue(appCtx, VARIABLES, getSampleJsonArrayOfJsonObjects(1), null))
+ }
+
+ private fun provideNetworkManager(): NetworkManager {
+ val metaData = CoreMetaData()
+ val deviceInfo = MockDeviceInfo(application, cleverTapInstanceConfig, "clevertapId", metaData)
+ val coreState = MockCoreState(appCtx, cleverTapInstanceConfig)
+ val callbackManager = CallbackManager(cleverTapInstanceConfig, deviceInfo)
+ val lockManager = CTLockManager()
+ val dbManager = DBManager(cleverTapInstanceConfig, lockManager)
+ val controllerManager =
+ ControllerManager(appCtx, cleverTapInstanceConfig, lockManager, callbackManager, deviceInfo, dbManager)
+ val cryptHandler = CryptHandler(0, AES, cleverTapInstanceConfig.accountId)
+ val localDataStore =
+ LocalDataStoreProvider.provideLocalDataStore(appCtx, cleverTapInstanceConfig, cryptHandler)
+ val inAppResponse =
+ InAppResponse(cleverTapInstanceConfig, controllerManager, true, coreState.storeRegistry, metaData)
+
+ return NetworkManager(
+ appCtx,
+ cleverTapInstanceConfig,
+ deviceInfo,
+ metaData,
+ ValidationResultStack(),
+ controllerManager,
+ dbManager,
+ ctApi,
+ callbackManager,
+ lockManager,
+ Validator(),
+ localDataStore,
+ cryptHandler,
+ inAppResponse
+ )
+ }
+}
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt
new file mode 100644
index 000000000..3d0e6878b
--- /dev/null
+++ b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt
@@ -0,0 +1,87 @@
+package com.clevertap.android.sdk.network.api
+
+import org.json.JSONArray
+import org.junit.*
+import org.junit.runner.*
+import org.mockito.*
+import org.robolectric.RobolectricTestRunner
+import kotlin.test.assertContains
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+
+@RunWith(RobolectricTestRunner::class)
+class CtApiTest {
+
+ private lateinit var ctApi: CtApi
+
+ @Before
+ fun setUpApi() {
+ ctApi = CtApiTestProvider.provideDefaultTestCtApi()
+ }
+
+ @Test
+ fun test_sendRequests_alwaysAttachDefaultHeaders() {
+ val expectedHeaders = mapOf(
+ "Content-Type" to "application/json; charset=utf-8",
+ "X-CleverTap-Account-ID" to CtApiTestProvider.ACCOUNT_ID,
+ "X-CleverTap-Token" to CtApiTestProvider.ACCOUNT_TOKEN
+ )
+
+ val sendQueueResponse = ctApi.sendQueue(false, getEmptyQueueBody())
+ assertEquals(expectedHeaders, sendQueueResponse.request.headers)
+
+ val handshakeResponse = ctApi.performHandshakeForDomain(false)
+ assertEquals(expectedHeaders, handshakeResponse.request.headers)
+
+ val sendVarsResponse = ctApi.defineVars(getEmptyQueueBody())
+ assertEquals(expectedHeaders, sendVarsResponse.request.headers)
+ }
+
+ @Test
+ fun test_sendQueueAndVariables_updateCurrentRequestTimestamp() {
+ ctApi.sendQueue(true, getEmptyQueueBody())
+ val timestamp = ctApi.currentRequestTimestampSeconds
+ Thread.sleep(1000)
+ ctApi.defineVars(getEmptyQueueBody())
+ assertNotEquals(timestamp, ctApi.currentRequestTimestampSeconds)
+ }
+
+ @Test
+ fun test_sendQueue_attachDefaultQueryParams() {
+ val request = ctApi.sendQueue(false, getEmptyQueueBody()).request
+ val urlString = request.url.toString()
+ assertContains(urlString, "os=Android")
+ assertContains(urlString, "t=${CtApiTestProvider.SDK_VERSION}")
+ assertContains(urlString, "z=${CtApiTestProvider.ACCOUNT_ID}")
+ assertContains(urlString, "ts=${ctApi.currentRequestTimestampSeconds}")
+ }
+
+ @Test
+ fun test_getActualDomain_definedRegion_returnFormattedDefaultDomains() {
+ assertEquals("${CtApiTestProvider.REGION}.${CtApiTestProvider.DEFAULT_DOMAIN}", ctApi.getActualDomain(false))
+ assertEquals(
+ "${CtApiTestProvider.REGION}-spiky.${CtApiTestProvider.DEFAULT_DOMAIN}",
+ ctApi.getActualDomain(true)
+ )
+ }
+
+ @Test
+ fun test_getActualDomain_noRegion_returnProxies() {
+ ctApi.region = null
+ assertEquals(CtApiTestProvider.PROXY_DOMAIN, ctApi.getActualDomain(false))
+ assertEquals(CtApiTestProvider.SPIKY_PROXY_DOMAIN, ctApi.getActualDomain(true))
+ }
+
+ @Test
+ fun test_getActualDomain_noRegion_noProxies_returnSavedDomains() {
+ ctApi.region = null
+ ctApi.proxyDomain = null
+ ctApi.spikyProxyDomain = null
+ assertEquals(CtApiTestProvider.DOMAIN, ctApi.getActualDomain(false))
+ assertEquals(CtApiTestProvider.SPIKY_DOMAIN, ctApi.getActualDomain(true))
+ }
+
+ private fun getEmptyQueueBody(): SendQueueRequestBody {
+ return SendQueueRequestBody(null, JSONArray())
+ }
+}
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTestProvider.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTestProvider.kt
new file mode 100644
index 000000000..7692e4caa
--- /dev/null
+++ b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTestProvider.kt
@@ -0,0 +1,60 @@
+package com.clevertap.android.sdk.network.api
+
+import com.clevertap.android.sdk.CleverTapInstanceConfig
+import com.clevertap.android.sdk.Constants
+import com.clevertap.android.sdk.Logger
+import com.clevertap.android.sdk.network.http.CtHttpClient
+import com.clevertap.android.sdk.network.http.MockHttpClient
+import org.mockito.*
+
+object CtApiTestProvider {
+
+ const val DEFAULT_DOMAIN = "domain.com"
+ const val DOMAIN = "new.domain.com"
+ const val REGION = "region"
+ const val SPIKY_DOMAIN = "new-spiky.domain.com"
+ const val PROXY_DOMAIN = "proxy-domain.com"
+ const val SPIKY_PROXY_DOMAIN = "proxy-spiky-domain.com"
+
+ const val ACCOUNT_ID = "accountId"
+ const val ACCOUNT_TOKEN = "accountToken"
+ const val SDK_VERSION = "x.x.x-test"
+
+ fun provideDefaultTestCtApi(): CtApi {
+
+ return CtApi(
+ MockHttpClient(),
+ DEFAULT_DOMAIN,
+ DOMAIN,
+ SPIKY_DOMAIN,
+ REGION,
+ PROXY_DOMAIN,
+ SPIKY_PROXY_DOMAIN,
+ ACCOUNT_ID,
+ ACCOUNT_TOKEN,
+ SDK_VERSION,
+ Mockito.mock(Logger::class.java),
+ "testCtApi"
+ )
+ }
+
+ fun provideTestCtApiForConfig(
+ config: CleverTapInstanceConfig,
+ httpClient: CtHttpClient = MockHttpClient()
+ ): CtApi {
+ return CtApi(
+ httpClient,
+ Constants.PRIMARY_DOMAIN,
+ null,
+ null,
+ config.accountRegion,
+ config.proxyDomain,
+ config.spikyProxyDomain,
+ config.accountId,
+ config.accountToken,
+ SDK_VERSION,
+ config.logger,
+ "testCtApi"
+ )
+ }
+}
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/http/MockHttpClient.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/http/MockHttpClient.kt
new file mode 100644
index 000000000..01165618c
--- /dev/null
+++ b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/http/MockHttpClient.kt
@@ -0,0 +1,19 @@
+package com.clevertap.android.sdk.network.http
+
+import java.net.HttpURLConnection
+
+class MockHttpClient(
+ var responseCode: Int = HttpURLConnection.HTTP_OK,
+ var responseHeaders: Map> = mapOf(),
+ var responseBody: String? = ""
+) : CtHttpClient {
+
+ var alwaysThrowOnExecute = false
+
+ override fun execute(request: Request): Response {
+ if (alwaysThrowOnExecute) {
+ throw RuntimeException("MockHttpClient exception on execute")
+ }
+ return Response(request, responseCode, responseHeaders, responseBody?.byteInputStream(Charsets.UTF_8)) {}
+ }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a4241ab6e..a4ef03bbe 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -48,7 +48,7 @@ powermock_classloading_xstream = "2.0.9"
powermock_core = "2.0.9"
powermock_module_junit4 = "2.0.9"
powermock_module_junit4_rule = "2.0.9"
-robolectric = "4.7.3"
+robolectric = "4.9"
jsonassert = "1.5.0"
xmlpull = "1.1.3.1"
mockk = "1.13.5"
From 0223bab59fc5f3022c74a00627740602ef14b54e Mon Sep 17 00:00:00 2001
From: Vassil Angelov
Date: Tue, 23 Jan 2024 14:52:56 +0200
Subject: [PATCH 02/29] Add TriggerManager to NetworkManagerTests
---
.../clevertap/android/sdk/network/NetworkManager.java | 5 +++--
.../android/sdk/network/NetworkManagerTest.kt | 11 ++++++++++-
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java
index 575c9c640..0abc28fb3 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/network/NetworkManager.java
@@ -398,8 +398,9 @@ private JSONObject getQueueHeader(Context context, @Nullable final String caller
// Add ddnd (Do Not Disturb)
header.put("ddnd",
- !(CTXtensions.areAppNotificationsEnabled(this.context) && (controllerManager.getPushProviders()
- .isNotificationSupported())));
+ !(CTXtensions.areAppNotificationsEnabled(this.context)
+ && (controllerManager.getPushProviders() == null
+ || controllerManager.getPushProviders().isNotificationSupported())));
// Add bk (Background Ping) if required
if (coreMetaData.isBgPing()) {
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt
index 4dd3293fd..f7677f9f7 100644
--- a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt
+++ b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/NetworkManagerTest.kt
@@ -14,6 +14,7 @@ import com.clevertap.android.sdk.db.DBManager
import com.clevertap.android.sdk.events.EventGroup.PUSH_NOTIFICATION_VIEWED
import com.clevertap.android.sdk.events.EventGroup.REGULAR
import com.clevertap.android.sdk.events.EventGroup.VARIABLES
+import com.clevertap.android.sdk.inapp.TriggerManager
import com.clevertap.android.sdk.network.api.CtApi
import com.clevertap.android.sdk.network.api.CtApiTestProvider
import com.clevertap.android.sdk.network.http.MockHttpClient
@@ -131,8 +132,16 @@ class NetworkManagerTest : BaseTestCase() {
val cryptHandler = CryptHandler(0, AES, cleverTapInstanceConfig.accountId)
val localDataStore =
LocalDataStoreProvider.provideLocalDataStore(appCtx, cleverTapInstanceConfig, cryptHandler)
+ val triggersManager = TriggerManager(appCtx, cleverTapInstanceConfig.accountId, deviceInfo)
val inAppResponse =
- InAppResponse(cleverTapInstanceConfig, controllerManager, true, coreState.storeRegistry, metaData)
+ InAppResponse(
+ cleverTapInstanceConfig,
+ controllerManager,
+ true,
+ coreState.storeRegistry,
+ triggersManager,
+ metaData
+ )
return NetworkManager(
appCtx,
From 2c88d10cd73c2a74564e196da00fb9019fa93cd3 Mon Sep 17 00:00:00 2001
From: anush
Date: Mon, 29 Jan 2024 12:37:08 +0530
Subject: [PATCH 03/29] task(SDK-3620) - Upgrades AGP to 8.2.1
---
clevertap-core/build.gradle | 2 ++
clevertap-core/src/androidTest/AndroidManifest.xml | 3 +--
clevertap-core/src/main/AndroidManifest.xml | 14 ++++++--------
clevertap-geofence/build.gradle | 4 ++++
clevertap-geofence/src/main/AndroidManifest.xml | 3 +--
clevertap-hms/build.gradle | 4 ++++
clevertap-hms/src/main/AndroidManifest.xml | 3 +--
clevertap-pushtemplates/build.gradle | 1 +
.../src/main/AndroidManifest.xml | 3 +--
clevertap-xps/build.gradle | 1 +
clevertap-xps/src/main/AndroidManifest.xml | 3 +--
gradle-scripts/checkstyle.gradle | 8 ++++----
gradle-scripts/commons.gradle | 2 +-
gradle-scripts/jacoco_root.gradle | 2 +-
gradle.properties | 3 +++
gradle/libs.versions.toml | 14 +++++++-------
gradle/wrapper/gradle-wrapper.properties | 2 +-
instantapp/src/main/AndroidManifest.xml | 1 -
sample/build.gradle | 8 ++++----
sample/src/main/AndroidManifest.xml | 3 +--
test_shared/build.gradle | 6 ++----
test_shared/src/main/AndroidManifest.xml | 3 +--
22 files changed, 48 insertions(+), 45 deletions(-)
diff --git a/clevertap-core/build.gradle b/clevertap-core/build.gradle
index a98b73729..2cbf5139d 100644
--- a/clevertap-core/build.gradle
+++ b/clevertap-core/build.gradle
@@ -28,6 +28,8 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
+ namespace 'com.clevertap.android.sdk'
+ testNamespace 'com.clevertap.demo'
}
dependencies {
diff --git a/clevertap-core/src/androidTest/AndroidManifest.xml b/clevertap-core/src/androidTest/AndroidManifest.xml
index 108978520..1ec2bc639 100644
--- a/clevertap-core/src/androidTest/AndroidManifest.xml
+++ b/clevertap-core/src/androidTest/AndroidManifest.xml
@@ -1,6 +1,5 @@
-
+
diff --git a/clevertap-core/src/main/AndroidManifest.xml b/clevertap-core/src/main/AndroidManifest.xml
index c7d6b5295..f3b7e5937 100644
--- a/clevertap-core/src/main/AndroidManifest.xml
+++ b/clevertap-core/src/main/AndroidManifest.xml
@@ -1,9 +1,7 @@
-
-
+
-
+
@@ -18,12 +16,12 @@
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar" />
@@ -32,12 +30,12 @@
diff --git a/clevertap-geofence/build.gradle b/clevertap-geofence/build.gradle
index f782531d3..41964366a 100644
--- a/clevertap-geofence/build.gradle
+++ b/clevertap-geofence/build.gradle
@@ -13,6 +13,10 @@ ext {
apply from: "../gradle-scripts/commons.gradle"
+android {
+ namespace 'com.clevertap.android.geofence'
+}
+
dependencies {
compileOnly (project(":clevertap-core"))
compileOnly (libs.play.services.location)
diff --git a/clevertap-geofence/src/main/AndroidManifest.xml b/clevertap-geofence/src/main/AndroidManifest.xml
index 93c8e0cff..a2880b881 100644
--- a/clevertap-geofence/src/main/AndroidManifest.xml
+++ b/clevertap-geofence/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
+
diff --git a/clevertap-hms/build.gradle b/clevertap-hms/build.gradle
index 3d24de644..0f4cb92c2 100644
--- a/clevertap-hms/build.gradle
+++ b/clevertap-hms/build.gradle
@@ -18,6 +18,10 @@ ext {
apply from: "../gradle-scripts/commons.gradle"
+android {
+ namespace 'com.clevertap.android.hms'
+}
+
dependencies {
compileOnly project(':clevertap-core')
implementation (libs.huawei.push)
diff --git a/clevertap-hms/src/main/AndroidManifest.xml b/clevertap-hms/src/main/AndroidManifest.xml
index 1a9d95a2d..b6e6eab14 100644
--- a/clevertap-hms/src/main/AndroidManifest.xml
+++ b/clevertap-hms/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
+
-
+
+
diff --git a/gradle-scripts/checkstyle.gradle b/gradle-scripts/checkstyle.gradle
index 187958598..5c8283a49 100644
--- a/gradle-scripts/checkstyle.gradle
+++ b/gradle-scripts/checkstyle.gradle
@@ -17,10 +17,10 @@ task Checkstyle(type: Checkstyle) {
showViolations true
include '**/*.java'
classpath = files()
- reports{
- html.enabled(true)
- html.destination = file("${buildDir}/reports/checkstyle/checkstyle-${project.name}.html")
- xml.enabled(false)
+ reports {
+ html.required.set(true)
+ xml.required.set(false)
+ html.outputLocation.set(file("${buildDir}/reports/checkstyle/checkstyle-${project.name}.html"))
}
}
diff --git a/gradle-scripts/commons.gradle b/gradle-scripts/commons.gradle
index d6bc660c6..09157767a 100644
--- a/gradle-scripts/commons.gradle
+++ b/gradle-scripts/commons.gradle
@@ -113,7 +113,7 @@ if (project.rootProject.file('local.properties').exists()) {
}
task sourcesJar(type: Jar) {
- baseName "$artifact"
+ archiveBaseName.set("$artifact")
from android.sourceSets.main.java.srcDirs
archiveClassifier.set('sources')
}
diff --git a/gradle-scripts/jacoco_root.gradle b/gradle-scripts/jacoco_root.gradle
index 292145687..52311501a 100644
--- a/gradle-scripts/jacoco_root.gradle
+++ b/gradle-scripts/jacoco_root.gradle
@@ -62,7 +62,7 @@ def createVariantCoverage(variant) {
description = "Generate Jacoco coverage reports for the ${variantName.capitalize()} build."
reports {
- html.enabled = true
+ html.required = true
}
def javaClasses = fileTree(dir: variant.javaCompileProvider.get().destinationDir, excludes: project.excludes)
diff --git a/gradle.properties b/gradle.properties
index 0544c35db..f06785253 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,2 +1,5 @@
+android.defaults.buildfeatures.buildconfig=true
+android.nonFinalResIds=false
+android.nonTransitiveRClass=false
android.useAndroidX=true
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a4ef03bbe..cea95850f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,12 +1,12 @@
[versions]
# Project
-android_compileSdk = "33"
-android_gradle_plugin = "7.4.2"
+android_compileSdk = "34"
+android_gradle_plugin = "8.2.1"
android_minSdk = "19"
-android_targetSdk = "33"
-kotlin_plugin = "1.7.20"
+android_targetSdk = "34"
+kotlin_plugin = "1.9.0"
sonarqube_plugin = "3.3"
-android_buildTools = "33.0.0"
+android_buildTools = "34.0.0"
detekt_gradle_plugin = "1.20.0-RC1"
firebase_gradle_crashlytics = "2.8.1"
@@ -85,10 +85,10 @@ gson = "2.8.6"
firebase_messaging = "23.0.6"
#GMS
-google_services = "4.3.3"
+google_services = "4.4.0"
#HMS Push Plugin/Lib
-agcp = "1.9.0.300"
+agcp = "1.9.1.300"
push = "6.11.0.300"
#Catch Exception
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 63823cb76..57f7924c1 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Thu May 18 16:22:56 IST 2023
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/instantapp/src/main/AndroidManifest.xml b/instantapp/src/main/AndroidManifest.xml
index 58b304928..4fee77ced 100644
--- a/instantapp/src/main/AndroidManifest.xml
+++ b/instantapp/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
diff --git a/sample/build.gradle b/sample/build.gradle
index 6ca29426b..5e740d7bb 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -13,11 +13,11 @@ allprojects {
}
android {
- compileSdkVersion 33
+ compileSdk 34
defaultConfig {
applicationId "com.clevertap.demo"
minSdkVersion 21
- targetSdkVersion 33
+ targetSdkVersion 34
versionCode 200005
versionName "1.6.2-full"
multiDexEnabled true
@@ -66,6 +66,7 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+ namespace 'com.clevertap.demo'
}
dependencies {
@@ -133,7 +134,6 @@ dependencies {
implementation "com.android.tools.build:gradle:4.2.1"
implementation "com.google.gms:google-services:4.3.3"// Google Services plugin
//classpath "com.github.dcendents:android-maven-gradle-plugin:$mavenPluginVersion"
- implementation "com.huawei.agconnect:agcp:1.6.5.300"// Huawei Push Plugin
implementation "org.jacoco:org.jacoco.core:0.8.4"
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
implementation "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1"*/
@@ -142,7 +142,7 @@ dependencies {
remoteImplementation("com.clevertap.android:clevertap-geofence-sdk:1.2.0")
remoteImplementation("com.clevertap.android:clevertap-xiaomi-sdk:1.5.2")
remoteImplementation("com.clevertap.android:push-templates:1.1.0")
- remoteImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.2")
+ remoteImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.3")
stagingImplementation("com.clevertap.android:clevertap-android-sdk:5.2.1")
stagingImplementation("com.clevertap.android:clevertap-geofence-sdk:1.2.0")
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index 5111b880d..5db1933d5 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-
+
diff --git a/test_shared/build.gradle b/test_shared/build.gradle
index 0b5f9bdee..694c439e5 100644
--- a/test_shared/build.gradle
+++ b/test_shared/build.gradle
@@ -4,14 +4,11 @@ plugins {
}
android {
- compileSdkVersion libs.versions.android.compileSdk.get().toInteger()
- buildToolsVersion libs.versions.android.buildTools.get()
+ compileSdk libs.versions.android.compileSdk.get().toInteger()
defaultConfig {
minSdkVersion libs.versions.android.minSdk.get().toInteger()
targetSdkVersion libs.versions.android.targetSdk.get().toInteger()
- versionCode 1
- versionName "1.0"
// testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
@@ -23,6 +20,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
+ namespace 'com.clevertap.android.shared.test'
}
dependencies {
diff --git a/test_shared/src/main/AndroidManifest.xml b/test_shared/src/main/AndroidManifest.xml
index c8aa01547..632a893db 100644
--- a/test_shared/src/main/AndroidManifest.xml
+++ b/test_shared/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-
+
Date: Mon, 29 Jan 2024 12:57:34 +0530
Subject: [PATCH 04/29] task(SDK-3620) - Resolves MissingClasses Detected while
Running R8 for core
---
clevertap-core/consumer-rules.pro | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clevertap-core/consumer-rules.pro b/clevertap-core/consumer-rules.pro
index 4ebde184c..f4c95017b 100644
--- a/clevertap-core/consumer-rules.pro
+++ b/clevertap-core/consumer-rules.pro
@@ -12,6 +12,6 @@
-keepnames class * implements android.os.Parcelable {
public static final ** CREATOR;
}
-
+-dontwarn com.clevertap.android.sdk.**
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
\ No newline at end of file
From dcd5bc49d663d28ff80ced4693ecc1b2ef70a00c Mon Sep 17 00:00:00 2001
From: anush
Date: Wed, 31 Jan 2024 15:14:57 +0530
Subject: [PATCH 05/29] task(SDK-3620) - Setup workflows to use JDK 17
---
.github/mini_flows/setup_jdk/action.yml | 4 ++--
.github/workflows/manually_validate.yml | 2 +-
.github/workflows/on_pr_from_develop_to_master.yml | 2 +-
.github/workflows/on_pr_from_task_to_develop.yml | 2 +-
.github/workflows/on_pr_merged_in_master.yml | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/mini_flows/setup_jdk/action.yml b/.github/mini_flows/setup_jdk/action.yml
index 0af016542..405cfd928 100644
--- a/.github/mini_flows/setup_jdk/action.yml
+++ b/.github/mini_flows/setup_jdk/action.yml
@@ -1,9 +1,9 @@
runs:
using: "composite"
steps:
- - name: Setup JDK 11
+ - name: Setup JDK 17
uses: actions/setup-java@v3
with:
- java-version: '11'
+ java-version: '17'
distribution: 'temurin'
# cache: gradle
diff --git a/.github/workflows/manually_validate.yml b/.github/workflows/manually_validate.yml
index 5944fd71a..86fdeb30b 100644
--- a/.github/workflows/manually_validate.yml
+++ b/.github/workflows/manually_validate.yml
@@ -77,7 +77,7 @@ jobs:
if: ${{ github.event.inputs.check_mandatory }}
uses: ./.github/mini_flows/mandatory_filechanges
- - name: Setup JDK 11.
+ - name: Setup JDK 17.
uses: ./.github/mini_flows/setup_jdk
- name: Run lint tests and Upload results
diff --git a/.github/workflows/on_pr_from_develop_to_master.yml b/.github/workflows/on_pr_from_develop_to_master.yml
index b464db365..329e53289 100644
--- a/.github/workflows/on_pr_from_develop_to_master.yml
+++ b/.github/workflows/on_pr_from_develop_to_master.yml
@@ -15,7 +15,7 @@ jobs:
- name: Checkout the code from Repo
uses: actions/checkout@v3
- - name: Setup JDK 11.
+ - name: Setup JDK 17.
uses: ./.github/mini_flows/setup_jdk
- name: Mandatory File Changes
diff --git a/.github/workflows/on_pr_from_task_to_develop.yml b/.github/workflows/on_pr_from_task_to_develop.yml
index 197484812..497a1b6a8 100644
--- a/.github/workflows/on_pr_from_task_to_develop.yml
+++ b/.github/workflows/on_pr_from_task_to_develop.yml
@@ -15,7 +15,7 @@ jobs:
- name: Checkout the code from Repo
uses: actions/checkout@v3
- - name: Setup JDK 11.
+ - name: Setup JDK 17.
uses: ./.github/mini_flows/setup_jdk
- name: Run lint tests and Upload results
diff --git a/.github/workflows/on_pr_merged_in_master.yml b/.github/workflows/on_pr_merged_in_master.yml
index a144cf4cf..b81f8f5f9 100644
--- a/.github/workflows/on_pr_merged_in_master.yml
+++ b/.github/workflows/on_pr_merged_in_master.yml
@@ -18,7 +18,7 @@ jobs:
- name: Checkout the code from Repo
uses: actions/checkout@v3
- - name: Setup JDK 11.
+ - name: Setup JDK 17.
uses: ./.github/mini_flows/setup_jdk
- name: Run lint tests and Upload results
From 192fef9d51e2ceb6e19f68f21a5b7d68ca223321 Mon Sep 17 00:00:00 2001
From: anush
Date: Wed, 31 Jan 2024 15:17:19 +0530
Subject: [PATCH 06/29] task(SDK-3620) - Resolve workflow issues
---
gradle.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gradle.properties b/gradle.properties
index f06785253..081b1d1ab 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,4 +2,4 @@ android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false
android.useAndroidX=true
-org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m
\ No newline at end of file
+org.gradle.jvmargs=-Xmx4g
\ No newline at end of file
From 1edf815d639fcab212880262daeb93667235771b Mon Sep 17 00:00:00 2001
From: anush
Date: Wed, 31 Jan 2024 15:39:24 +0530
Subject: [PATCH 07/29] task(SDK-3620) - Resolve workflow issues
---
test_shared/build.gradle | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/test_shared/build.gradle b/test_shared/build.gradle
index 694c439e5..a26a1fcf4 100644
--- a/test_shared/build.gradle
+++ b/test_shared/build.gradle
@@ -20,6 +20,14 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_17
+ }
+
namespace 'com.clevertap.android.shared.test'
}
From 0d9016b974cd2bc17fc097f852f1e8f2d29bda18 Mon Sep 17 00:00:00 2001
From: Anush-Shand <127097095+Anush-Shand@users.noreply.github.com>
Date: Fri, 9 Feb 2024 10:07:35 +0530
Subject: [PATCH 08/29] Task/sdk 3639/refactor geofence unit tests (#547)
* task(SDK-3639) - Refactors unit tests to remove powermock dependency, part 1
* task(SDK-3639) - Refactors unit tests to remove powermock dependency, part 2
* task(SDK-3639) - Refactors unit tests to remove powermock dependency, updates mockito version
* task(SDK-3639) - Resolves case where CTGeofenceTaskManager unit tests were failing when run all together
---
clevertap-geofence/build.gradle | 1 -
.../android/geofence/CTGeofenceAPITest.java | 90 +++----
.../geofence/CTGeofenceBootReceiverTest.java | 180 ++++++--------
.../geofence/CTGeofenceFactoryTest.java | 82 +++----
.../geofence/CTGeofenceReceiverTest.java | 66 ++----
.../geofence/CTGeofenceSettingsTest.java | 27 +--
.../geofence/CTGeofenceTaskManagerTest.java | 106 ++-------
.../geofence/CTLocationFactoryTest.java | 84 +++----
.../CTLocationUpdateReceiverTest.java | 69 ++----
.../geofence/GeofenceUpdateTaskTest.java | 63 ++---
.../geofence/GoogleGeofenceAdapterTest.java | 130 ++++-------
.../geofence/GoogleLocationAdapterTest.java | 105 ++++-----
.../geofence/LocationUpdateTaskTest.java | 197 ++++++++--------
.../geofence/PendingIntentFactoryTest.java | 17 --
.../geofence/PushGeofenceEventTaskTest.java | 221 +++++++++---------
.../geofence/PushLocationEventTaskTest.java | 133 +++++------
.../clevertap/android/geofence/UtilsTest.java | 221 +++++++++---------
gradle/libs.versions.toml | 15 +-
test_shared/build.gradle | 2 +-
19 files changed, 738 insertions(+), 1071 deletions(-)
diff --git a/clevertap-geofence/build.gradle b/clevertap-geofence/build.gradle
index f782531d3..faa601242 100644
--- a/clevertap-geofence/build.gradle
+++ b/clevertap-geofence/build.gradle
@@ -33,7 +33,6 @@ dependencies {
testImplementation (libs.androidx.appcompat)
testImplementation (libs.firebase.messaging)
- testImplementation (libs.bundles.powermock)
testImplementation (libs.catch.exception)
testImplementation (project(":clevertap-core"))
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceAPITest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceAPITest.java
index bdefd792b..507655385 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceAPITest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceAPITest.java
@@ -3,17 +3,12 @@
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static com.clevertap.android.geofence.CTGeofenceConstants.DEFAULT_LATITUDE;
import static com.clevertap.android.geofence.CTGeofenceConstants.DEFAULT_LONGITUDE;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.Manifest;
import android.app.PendingIntent;
-import android.content.Context;
import android.location.Location;
import com.clevertap.android.geofence.fakes.GeofenceJSON;
import com.clevertap.android.geofence.interfaces.CTGeofenceAdapter;
@@ -21,30 +16,12 @@
import com.clevertap.android.geofence.interfaces.CTLocationAdapter;
import com.clevertap.android.geofence.interfaces.CTLocationUpdatesListener;
import com.clevertap.android.sdk.CleverTapAPI;
-import com.clevertap.android.sdk.GeofenceCallback;
import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import org.json.JSONObject;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.Shadows;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CleverTapAPI.class, Utils.class, CTLocationFactory.class, CTGeofenceFactory.class
- , Executors.class, FileUtils.class})
+
public class CTGeofenceAPITest extends BaseTestCase {
@Mock
@@ -59,12 +36,11 @@ public class CTGeofenceAPITest extends BaseTestCase {
@Mock
public CTLocationAdapter locationAdapter;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
+ private MockedStatic ctGeofenceFactoryMockedStatic;
- private Location location;
+ private MockedStatic ctLocationFactoryMockedStatic;
- private Logger logger;
+ private MockedStatic utilsMockedStatic;
@After
public void cleanup() throws NoSuchFieldException, IllegalAccessException {
@@ -72,21 +48,33 @@ public void cleanup() throws NoSuchFieldException, IllegalAccessException {
Field field = CTGeofenceAPI.class.getDeclaredField("ctGeofenceAPI");
field.setAccessible(true);
field.set(instance, null);
+
+ CTGeofenceTaskManager taskManagerInstance = CTGeofenceTaskManager.getInstance();
+ Field fieldTaskManager = CTGeofenceTaskManager.class.getDeclaredField("taskManager");
+ fieldTaskManager.setAccessible(true);
+ fieldTaskManager.set(taskManagerInstance, null);
+
+ ctLocationFactoryMockedStatic.close();
+ ctGeofenceFactoryMockedStatic.close();
+ utilsMockedStatic.close();
+
}
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(Utils.class, CleverTapAPI.class, CTLocationFactory.class
- , CTGeofenceFactory.class, Executors.class, FileUtils.class);
+ MockitoAnnotations.openMocks(this);
super.setUp();
- location = new Location("");
+ ctLocationFactoryMockedStatic = mockStatic(CTLocationFactory.class);
+ ctLocationFactoryMockedStatic.when(() -> CTLocationFactory.createLocationAdapter(application))
+ .thenReturn(locationAdapter);
- when(CTLocationFactory.createLocationAdapter(application)).thenReturn(locationAdapter);
- when(CTGeofenceFactory.createGeofenceAdapter(application)).thenReturn(geofenceAdapter);
+ ctGeofenceFactoryMockedStatic = mockStatic(CTGeofenceFactory.class);
+ ctGeofenceFactoryMockedStatic.when(() -> CTGeofenceFactory.createGeofenceAdapter(application))
+ .thenReturn(geofenceAdapter);
+ utilsMockedStatic = Mockito.mockStatic(Utils.class);
}
@Test
@@ -94,25 +82,26 @@ public void testDeactivate() {
CTGeofenceAPI ctGeofenceAPI = CTGeofenceAPI.getInstance(application);
CTGeofenceTaskManager.getInstance().setExecutorService(executorService);
- ctGeofenceAPI.deactivate();
+ try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) {
+ ctGeofenceAPI.deactivate();
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(executorService).submit(argumentCaptor.capture());
- ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
- verify(executorService).submit(argumentCaptor.capture());
+ PendingIntent geofenceMonitoring = PendingIntentFactory.getPendingIntent(application,
+ PendingIntentFactory.PENDING_INTENT_GEOFENCE, FLAG_UPDATE_CURRENT);
+ PendingIntent locationUpdates = PendingIntentFactory.getPendingIntent(application,
+ PendingIntentFactory.PENDING_INTENT_LOCATION, FLAG_UPDATE_CURRENT);
- PendingIntent geofenceMonitoring = PendingIntentFactory.getPendingIntent(application,
- PendingIntentFactory.PENDING_INTENT_GEOFENCE, FLAG_UPDATE_CURRENT);
- PendingIntent locationUpdates = PendingIntentFactory.getPendingIntent(application,
- PendingIntentFactory.PENDING_INTENT_LOCATION, FLAG_UPDATE_CURRENT);
+ argumentCaptor.getValue().run();
- argumentCaptor.getValue().run();
+ verify(geofenceAdapter).stopGeofenceMonitoring(geofenceMonitoring);
+ verify(locationAdapter).removeLocationUpdates(locationUpdates);
- verify(geofenceAdapter).stopGeofenceMonitoring(geofenceMonitoring);
- verify(locationAdapter).removeLocationUpdates(locationUpdates);
+ fileUtilsMockedStatic.verify(
+ () -> FileUtils.deleteDirectory(any(), FileUtils.getCachedDirName(application)));
- PowerMockito.verifyStatic(FileUtils.class);
- FileUtils.deleteDirectory(any(Context.class), FileUtils.getCachedDirName(application));
-
- assertFalse(ctGeofenceAPI.isActivated());
+ assertFalse(ctGeofenceAPI.isActivated());
+ }
}
@Test
@@ -446,11 +435,8 @@ public void onGeofenceExitedEvent(JSONObject geofenceExitedEventProperties) {
@Test
public void testSetCtLocationUpdatesListener() {
- CTLocationUpdatesListener listener = new CTLocationUpdatesListener() {
- @Override
- public void onLocationUpdates(Location location) {
+ CTLocationUpdatesListener listener = location -> {
- }
};
CTGeofenceAPI ctGeofenceAPI = CTGeofenceAPI.getInstance(application);
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceBootReceiverTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceBootReceiverTest.java
index c3152fb90..99e8033b4 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceBootReceiverTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceBootReceiverTest.java
@@ -1,31 +1,15 @@
package com.clevertap.android.geofence;
+import static com.clevertap.android.geofence.CTGeofenceAPI.GEOFENCE_LOG_TAG;
import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Intent;
-import java.util.concurrent.Callable;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.mockito.invocation.*;
-import org.mockito.stubbing.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, CTGeofenceTaskManager.class, Utils.class})
+
public class CTGeofenceBootReceiverTest extends BaseTestCase {
@Mock
@@ -34,28 +18,13 @@ public class CTGeofenceBootReceiverTest extends BaseTestCase {
@Mock
public BroadcastReceiver.PendingResult pendingResult;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
@Mock
- public CTGeofenceTaskManager taskManager;
-
private Logger logger;
@Before
public void setUp() throws Exception {
-
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, CTGeofenceTaskManager.class, Utils.class);
-
+ MockitoAnnotations.openMocks(this);
super.setUp();
-
- when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
- when(CTGeofenceAPI.getLogger()).thenReturn(logger);
-
- PowerMockito.when(CTGeofenceTaskManager.getInstance()).thenReturn(taskManager);
-
}
@Test
@@ -63,7 +32,6 @@ public void testOnReceiveWhenIntentIstNull() {
CTGeofenceBootReceiver receiver = new CTGeofenceBootReceiver();
CTGeofenceBootReceiver spy = Mockito.spy(receiver);
- // todo useful
spy.onReceive(application, null);
@@ -81,32 +49,31 @@ public void testOnReceiveWhenIntentNotNullTC1() {
when(spy.goAsync()).thenReturn(pendingResult);
final Boolean[] isFinished = {false};
-
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- isFinished[0] = true;
- return null;
- }
- }).when(pendingResult).finish();
-
- PowerMockito.when(Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
- .thenReturn(true);
- PowerMockito.when(Utils.hasBackgroundLocationPermission(application)).thenReturn(true);
- PowerMockito.when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
-
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- spy.onReceive(application, intent);
+ doAnswer(invocation -> {
+ isFinished[0] = true;
+ return null;
+ }).when(pendingResult).finish();
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinished[0];
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(() -> Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
+ .thenReturn(true);
+ utilsMockedStatic.when(() -> Utils.hasBackgroundLocationPermission(application))
+ .thenReturn(true);
+ utilsMockedStatic.when(() -> Utils.initCTGeofenceApiIfRequired(application))
+ .thenReturn(true);
+
+ try (MockedStatic ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class)) {
+ ctGeofenceAPIMockedStatic.when(CTGeofenceAPI::getLogger).thenReturn(logger);
+
+ spy.onReceive(application, intent);
+ await().until(() -> isFinished[0]);
+ verify(CTGeofenceAPI.getLogger()).debug(GEOFENCE_LOG_TAG,
+ "onReceive called after " + "device reboot");
+ verify(pendingResult).finish();
}
- });
-
- verify(pendingResult).finish();
+ }
}
@Test
@@ -119,72 +86,79 @@ public void testOnReceiveWhenIntentNotNullTC2() {
when(spy.goAsync()).thenReturn(pendingResult);
final Boolean[] isFinished = {false};
-
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- isFinished[0] = true;
- return null;
- }
- }).when(pendingResult).finish();
-
- PowerMockito.when(Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
- .thenReturn(true);
- PowerMockito.when(Utils.hasBackgroundLocationPermission(application)).thenReturn(true);
- PowerMockito.when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(false);
-
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
+ doAnswer(invocation -> {
+ isFinished[0] = true;
+ return null;
+ }).when(pendingResult).finish();
- spy.onReceive(application, intent);
-
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinished[0];
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(() -> Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
+ .thenReturn(true);
+ utilsMockedStatic.when(() -> Utils.hasBackgroundLocationPermission(application))
+ .thenReturn(true);
+ utilsMockedStatic.when(() -> Utils.initCTGeofenceApiIfRequired(application))
+ .thenReturn(false);
+
+ try (MockedStatic ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class)) {
+ ctGeofenceAPIMockedStatic.when(CTGeofenceAPI::getLogger).thenReturn(logger);
+ spy.onReceive(application, intent);
+ await().until(() -> isFinished[0]);
+
+ verify(CTGeofenceAPI.getLogger()).debug(GEOFENCE_LOG_TAG,
+ "onReceive called after " + "device reboot");
+ verifyNoMoreInteractions(CTGeofenceAPI.getLogger());
+ verify(pendingResult).finish();
}
- });
-
- verify(pendingResult).finish();
+ }
}
@Test
public void testOnReceiveWhenIntentNotNullTC3() {
-
// when ACCESS_FINE_LOCATION permission missing
-
CTGeofenceBootReceiver receiver = new CTGeofenceBootReceiver();
CTGeofenceBootReceiver spy = Mockito.spy(receiver);
-
- PowerMockito.when(Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
- .thenReturn(false);
-
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- spy.onReceive(application, intent);
-
- verify(spy, never()).goAsync();
-
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(() -> Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
+ .thenReturn(false);
+ try (MockedStatic ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class)) {
+ ctGeofenceAPIMockedStatic.when(CTGeofenceAPI::getLogger).thenReturn(logger);
+ spy.onReceive(application, intent);
+ verify(CTGeofenceAPI.getLogger()).debug(GEOFENCE_LOG_TAG,
+ "onReceive called after " + "device reboot");
+ verify(CTGeofenceAPI.getLogger()).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "We don't have ACCESS_FINE_LOCATION permission! Not registering "
+ + "geofences and location updates after device reboot");
+ verify(spy, never()).goAsync();
+ }
+ }
}
@Test
public void testOnReceiveWhenIntentNotNullTC4() {
-
// when ACCESS_BACKGROUND_LOCATION permission missing
-
CTGeofenceBootReceiver receiver = new CTGeofenceBootReceiver();
CTGeofenceBootReceiver spy = Mockito.spy(receiver);
-
- PowerMockito.when(Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
- .thenReturn(true);
- PowerMockito.when(Utils.hasBackgroundLocationPermission(application))
- .thenReturn(false);
-
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- spy.onReceive(application, intent);
-
- verify(spy, never()).goAsync();
-
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(() -> Utils.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION))
+ .thenReturn(true);
+ utilsMockedStatic.when(() -> Utils.hasBackgroundLocationPermission(application))
+ .thenReturn(false);
+ try (MockedStatic ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class)) {
+ ctGeofenceAPIMockedStatic.when(CTGeofenceAPI::getLogger).thenReturn(logger);
+ spy.onReceive(application, intent);
+ verify(CTGeofenceAPI.getLogger()).debug(GEOFENCE_LOG_TAG,
+ "onReceive called after " + "device reboot");
+ verify(CTGeofenceAPI.getLogger()).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "We don't have ACCESS_BACKGROUND_LOCATION permission! not registering "
+ + "geofences and location updates after device reboot");
+ spy.onReceive(application, intent);
+ verify(spy, never()).goAsync();
+ }
+ }
}
-
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceFactoryTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceFactoryTest.java
index 8b385c2d1..52e80889b 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceFactoryTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceFactoryTest.java
@@ -1,84 +1,84 @@
package com.clevertap.android.geofence;
-
-import static org.powermock.api.mockito.PowerMockito.when;
-
import com.clevertap.android.geofence.interfaces.CTGeofenceAdapter;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-@Ignore
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({Utils.class, GoogleApiAvailability.class})
public class CTGeofenceFactoryTest extends BaseTestCase {
@Mock
public GoogleApiAvailability googleApiAvailability;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(Utils.class, GoogleApiAvailability.class);
+ MockitoAnnotations.openMocks(this);
super.setUp();
- when(GoogleApiAvailability.getInstance()).thenReturn(googleApiAvailability);
}
@Test
public void testCreateGeofenceAdapterTC1() {
// when all dependencies available
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(true);
- when(googleApiAvailability.isGooglePlayServicesAvailable(application)).thenReturn(ConnectionResult.SUCCESS);
- CTGeofenceAdapter geofenceAdapter = CTGeofenceFactory.createGeofenceAdapter(application);
- Assert.assertNotNull(geofenceAdapter);
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+
+ try (MockedStatic googleApiAvailabilityMockedStatic = Mockito.mockStatic(
+ GoogleApiAvailability.class)) {
+ googleApiAvailabilityMockedStatic.when(GoogleApiAvailability::getInstance)
+ .thenReturn(googleApiAvailability);
+ Mockito.when(googleApiAvailability.isGooglePlayServicesAvailable(application))
+ .thenReturn(ConnectionResult.SUCCESS);
+ CTGeofenceAdapter geofenceAdapter = CTGeofenceFactory.createGeofenceAdapter(application);
+ Assert.assertNotNull(geofenceAdapter);
+ }
+ }
}
@Test(expected = IllegalStateException.class)
public void testCreateGeofenceAdapterTC2() {
-
// when play service apk not available
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(true);
- when(googleApiAvailability.isGooglePlayServicesAvailable(application))
- .thenReturn(ConnectionResult.SERVICE_MISSING);
- CTGeofenceFactory.createGeofenceAdapter(application);
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+ try (MockedStatic googleApiAvailabilityMockedStatic = Mockito.mockStatic(
+ GoogleApiAvailability.class)) {
+ googleApiAvailabilityMockedStatic.when(GoogleApiAvailability::getInstance)
+ .thenReturn(googleApiAvailability);
+ Mockito.when(googleApiAvailability.isGooglePlayServicesAvailable(application))
+ .thenReturn(ConnectionResult.SERVICE_MISSING);
+ CTGeofenceFactory.createGeofenceAdapter(application);
+ }
+ }
}
@Test(expected = IllegalStateException.class)
public void testCreateGeofenceAdapterTC3() {
-
// when play service apk is disabled
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(true);
- when(googleApiAvailability.isGooglePlayServicesAvailable(application))
- .thenReturn(ConnectionResult.SERVICE_DISABLED);
- CTGeofenceFactory.createGeofenceAdapter(application);
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+ try (MockedStatic googleApiAvailabilityMockedStatic = Mockito.mockStatic(
+ GoogleApiAvailability.class)) {
+ googleApiAvailabilityMockedStatic.when(GoogleApiAvailability::getInstance)
+ .thenReturn(googleApiAvailability);
+ Mockito.when(googleApiAvailability.isGooglePlayServicesAvailable(application))
+ .thenReturn(ConnectionResult.SERVICE_DISABLED);
+ CTGeofenceFactory.createGeofenceAdapter(application);
+ }
+ }
}
@Test(expected = IllegalStateException.class)
public void testCreateGeofenceAdapterTC4() {
-
// when fused location dependency not available
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(false);
- CTGeofenceFactory.createGeofenceAdapter(application);
- }
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+ CTGeofenceFactory.createGeofenceAdapter(application);
+ }
+ }
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceReceiverTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceReceiverTest.java
index 576df519a..10688a510 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceReceiverTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceReceiverTest.java
@@ -2,29 +2,12 @@
import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.content.BroadcastReceiver;
import android.content.Intent;
-import java.util.concurrent.Callable;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.mockito.invocation.*;
-import org.mockito.stubbing.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, CTGeofenceTaskManager.class})
+
public class CTGeofenceReceiverTest extends BaseTestCase {
@Mock
@@ -33,37 +16,20 @@ public class CTGeofenceReceiverTest extends BaseTestCase {
@Mock
public BroadcastReceiver.PendingResult pendingResult;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
@Mock
- public CTGeofenceTaskManager taskManager;
-
private Logger logger;
@Before
public void setUp() throws Exception {
-
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, CTGeofenceTaskManager.class);
-
+ MockitoAnnotations.openMocks(this);
super.setUp();
-
- when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
- when(CTGeofenceAPI.getLogger()).thenReturn(logger);
-
- PowerMockito.when(CTGeofenceTaskManager.getInstance()).thenReturn(taskManager);
-
}
@Test
public void testOnReceiveWhenIntentIsNull() {
CTGeofenceReceiver receiver = new CTGeofenceReceiver();
CTGeofenceReceiver spy = Mockito.spy(receiver);
-
spy.onReceive(application, null);
-
verify(spy, never()).goAsync();
}
@@ -75,25 +41,23 @@ public void testOnReceiveWhenIntentNotNull() {
final Boolean[] isFinished = {false};
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- isFinished[0] = true;
- return null;
- }
+ doAnswer(invocation -> {
+ isFinished[0] = true;
+ return null;
}).when(pendingResult).finish();
- Intent intent = new Intent();
- spy.onReceive(application, intent);
+ try (MockedStatic ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class)) {
+ ctGeofenceAPIMockedStatic.when(CTGeofenceAPI::getLogger).thenReturn(logger);
+
+ Intent intent = new Intent();
+ spy.onReceive(application, intent);
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinished[0];
- }
- });
+ await().until(() -> isFinished[0]);
- verify(pendingResult).finish();
+ verify(CTGeofenceAPI.getLogger()).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG, "Geofence receiver called");
+ verify(CTGeofenceAPI.getLogger()).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG, "Returning from Geofence receiver");
+ verify(pendingResult).finish();
+ }
}
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceSettingsTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceSettingsTest.java
index 8ab066186..6921dc1cf 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceSettingsTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceSettingsTest.java
@@ -8,30 +8,13 @@
import static org.junit.Assert.*;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({Utils.class})
public class CTGeofenceSettingsTest extends BaseTestCase {
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(Utils.class);
+ MockitoAnnotations.openMocks(this);
super.setUp();
}
@@ -50,12 +33,12 @@ public void testCustomSettings() {
.setSmallestDisplacement(780)
.build();
- assertEquals(false, customSettings.isBackgroundLocationUpdatesEnabled());
+ assertFalse(customSettings.isBackgroundLocationUpdatesEnabled());
assertEquals(ACCURACY_MEDIUM, customSettings.getLocationAccuracy());
assertEquals(FETCH_CURRENT_LOCATION_PERIODIC, customSettings.getLocationFetchMode());
assertEquals(Logger.INFO, customSettings.getLogLevel());
assertEquals(98, customSettings.getGeofenceMonitoringCount());
- assertEquals(null, customSettings.getId());
+ assertNull(customSettings.getId());
assertEquals(2000000, customSettings.getInterval());
assertEquals(1900000, customSettings.getFastestInterval());
assertEquals(780, customSettings.getSmallestDisplacement(), 0);
@@ -78,12 +61,12 @@ public void testDefaultSettings() {
CTGeofenceSettings defaultSettings = new CTGeofenceSettings.Builder().build();
- assertEquals(true, defaultSettings.isBackgroundLocationUpdatesEnabled());
+ assertTrue(defaultSettings.isBackgroundLocationUpdatesEnabled());
assertEquals(ACCURACY_HIGH, defaultSettings.getLocationAccuracy());
assertEquals(FETCH_LAST_LOCATION_PERIODIC, defaultSettings.getLocationFetchMode());
assertEquals(Logger.DEBUG, defaultSettings.getLogLevel());
assertEquals(DEFAULT_GEO_MONITOR_COUNT, defaultSettings.getGeofenceMonitoringCount());
- assertEquals(null, defaultSettings.getId());
+ assertNull(defaultSettings.getId());
assertEquals(GoogleLocationAdapter.INTERVAL_IN_MILLIS, defaultSettings.getInterval());
assertEquals(GoogleLocationAdapter.INTERVAL_FASTEST_IN_MILLIS, defaultSettings.getFastestInterval());
assertEquals(GoogleLocationAdapter.SMALLEST_DISPLACEMENT_IN_METERS, defaultSettings.getSmallestDisplacement(),
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceTaskManagerTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceTaskManagerTest.java
index a1db468c1..81b110ece 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceTaskManagerTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTGeofenceTaskManagerTest.java
@@ -3,25 +3,13 @@
import static org.awaitility.Awaitility.await;
import com.clevertap.android.geofence.interfaces.CTGeofenceTask;
-import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.junit.*;
-import org.junit.runner.*;
-import org.mockito.*;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
public class CTGeofenceTaskManagerTest extends BaseTestCase {
@Before
public void setUp() throws Exception {
-
- MockitoAnnotations.initMocks(this);
super.setUp();
}
@@ -38,19 +26,9 @@ public void testGetInstance() {
public void testPostAsyncSafelyRunnable() {
final boolean[] isFinish = {false};
- Future> future = CTGeofenceTaskManager.getInstance().postAsyncSafely("", new Runnable() {
- @Override
- public void run() {
- isFinish[0] = true;
- }
- });
+ Future> future = CTGeofenceTaskManager.getInstance().postAsyncSafely("", () -> isFinish[0] = true);
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinish[0];
- }
- });
+ await().until(() -> isFinish[0]);
Assert.assertNotNull(future);
}
@@ -62,26 +40,11 @@ public void testPostAsyncSafelyRunnableFlatCall() {
final boolean[] isFinish = {false, false};
final Future>[] flatFuture = {null, null};
- flatFuture[0] = CTGeofenceTaskManager.getInstance().postAsyncSafely("", new Runnable() {
- @Override
- public void run() {
- isFinish[0] = true;
- }
- });
+ flatFuture[0] = CTGeofenceTaskManager.getInstance().postAsyncSafely("", () -> isFinish[0] = true);
- flatFuture[1] = CTGeofenceTaskManager.getInstance().postAsyncSafely("nested", new Runnable() {
- @Override
- public void run() {
- isFinish[1] = true;
- }
- });
+ flatFuture[1] = CTGeofenceTaskManager.getInstance().postAsyncSafely("nested", () -> isFinish[1] = true);
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinish[0] && isFinish[1];
- }
- });
+ await().until(() -> isFinish[0] && isFinish[1]);
Assert.assertNotNull(flatFuture[0]);
Assert.assertNotNull(flatFuture[1]);
@@ -94,26 +57,11 @@ public void testPostAsyncSafelyRunnableNestedCall() {
final boolean[] isFinish = {false};
final Future>[] nestedFuture = {null};
- Future> future = CTGeofenceTaskManager.getInstance().postAsyncSafely("", new Runnable() {
- @Override
- public void run() {
+ Future> future = CTGeofenceTaskManager.getInstance().postAsyncSafely("",
+ () -> nestedFuture[0] = CTGeofenceTaskManager.getInstance().postAsyncSafely("nested",
+ () -> isFinish[0] = true));
- nestedFuture[0] = CTGeofenceTaskManager.getInstance().postAsyncSafely("nested", new Runnable() {
- @Override
- public void run() {
- isFinish[0] = true;
- }
- });
-
- }
- });
-
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinish[0];
- }
- });
+ await().until(() -> isFinish[0]);
Assert.assertNotNull(future);
Assert.assertNull(nestedFuture[0]);
@@ -135,12 +83,7 @@ public void setOnCompleteListener(OnCompleteListener onCompleteListener) {
}
});
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinish[0];
- }
- });
+ await().until(() -> isFinish[0]);
Assert.assertNotNull(future);
}
@@ -177,12 +120,7 @@ public void setOnCompleteListener(OnCompleteListener onCompleteListener) {
}
});
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinish[0] && isFinish[1];
- }
- });
+ await().until(() -> isFinish[0] && isFinish[1]);
Assert.assertNotNull(flatFuture[0]);
Assert.assertNotNull(flatFuture[1]);
@@ -219,12 +157,7 @@ public void setOnCompleteListener(OnCompleteListener onCompleteListener) {
}
});
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinish[0];
- }
- });
+ await().until(() -> isFinish[0]);
Assert.assertNotNull(future);
Assert.assertNull(nestedFuture[0]);
@@ -241,12 +174,8 @@ public void testPostAsyncSafelyTaskRunnableNestedCall() {
@Override
public void execute() {
- nestedFuture[0] = CTGeofenceTaskManager.getInstance().postAsyncSafely("nested", new Runnable() {
- @Override
- public void run() {
- isFinish[0] = true;
- }
- });
+ nestedFuture[0] = CTGeofenceTaskManager.getInstance().postAsyncSafely("nested",
+ () -> isFinish[0] = true);
}
@@ -256,12 +185,7 @@ public void setOnCompleteListener(OnCompleteListener onCompleteListener) {
}
});
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinish[0];
- }
- });
+ await().until(() -> isFinish[0]);
Assert.assertNotNull(future);
Assert.assertNull(nestedFuture[0]);
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationFactoryTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationFactoryTest.java
index 27a795c17..dd24a3f73 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationFactoryTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationFactoryTest.java
@@ -1,83 +1,85 @@
package com.clevertap.android.geofence;
-import static org.powermock.api.mockito.PowerMockito.when;
-
import com.clevertap.android.geofence.interfaces.CTLocationAdapter;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@Ignore
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({Utils.class, GoogleApiAvailability.class})
+
public class CTLocationFactoryTest extends BaseTestCase {
@Mock
public GoogleApiAvailability googleApiAvailability;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(Utils.class, GoogleApiAvailability.class);
+ MockitoAnnotations.openMocks(this);
super.setUp();
- when(GoogleApiAvailability.getInstance()).thenReturn(googleApiAvailability);
}
@Test
public void testCreateLocationAdapterTC1() {
// when all dependencies available
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(true);
- when(googleApiAvailability.isGooglePlayServicesAvailable(application)).thenReturn(ConnectionResult.SUCCESS);
-
- CTLocationAdapter LocationAdapter = CTLocationFactory.createLocationAdapter(application);
- Assert.assertNotNull(LocationAdapter);
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+
+ try (MockedStatic googleApiAvailabilityMockedStatic = Mockito.mockStatic(
+ GoogleApiAvailability.class)) {
+ googleApiAvailabilityMockedStatic.when(GoogleApiAvailability::getInstance)
+ .thenReturn(googleApiAvailability);
+ Mockito.when(googleApiAvailability.isGooglePlayServicesAvailable(application))
+ .thenReturn(ConnectionResult.SUCCESS);
+
+ CTLocationAdapter LocationAdapter = CTLocationFactory.createLocationAdapter(application);
+ Assert.assertNotNull(LocationAdapter);
+ }
+ }
}
@Test(expected = IllegalStateException.class)
public void testCreateLocationAdapterTC2() {
// when play service apk not available
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(true);
- when(googleApiAvailability.isGooglePlayServicesAvailable(application))
- .thenReturn(ConnectionResult.SERVICE_MISSING);
-
- CTLocationFactory.createLocationAdapter(application);
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+ try (MockedStatic googleApiAvailabilityMockedStatic = Mockito.mockStatic(
+ GoogleApiAvailability.class)) {
+ googleApiAvailabilityMockedStatic.when(GoogleApiAvailability::getInstance)
+ .thenReturn(googleApiAvailability);
+ Mockito.when(googleApiAvailability.isGooglePlayServicesAvailable(application))
+ .thenReturn(ConnectionResult.SERVICE_MISSING);
+ CTLocationFactory.createLocationAdapter(application);
+ }
+ }
}
@Test(expected = IllegalStateException.class)
public void testCreateLocationAdapterTC3() {
// when play service apk is disabled
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(true);
- when(googleApiAvailability.isGooglePlayServicesAvailable(application))
- .thenReturn(ConnectionResult.SERVICE_DISABLED);
-
- CTLocationFactory.createLocationAdapter(application);
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+ try (MockedStatic googleApiAvailabilityMockedStatic = Mockito.mockStatic(
+ GoogleApiAvailability.class)) {
+ googleApiAvailabilityMockedStatic.when(GoogleApiAvailability::getInstance)
+ .thenReturn(googleApiAvailability);
+ Mockito.when(googleApiAvailability.isGooglePlayServicesAvailable(application))
+ .thenReturn(ConnectionResult.SERVICE_DISABLED);
+
+ CTLocationFactory.createLocationAdapter(application);
+ }
+ }
}
@Test(expected = IllegalStateException.class)
public void testCreateLocationAdapterTC4() {
// when fused location dependency not available
- when(Utils.isFusedLocationApiDependencyAvailable()).thenReturn(false);
- CTLocationFactory.createLocationAdapter(application);
+ try (MockedStatic utilsMockedStatic = Mockito.mockStatic(Utils.class)) {
+ utilsMockedStatic.when(Utils::isFusedLocationApiDependencyAvailable).thenReturn(true);
+ CTLocationFactory.createLocationAdapter(application);
+ }
}
-
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationUpdateReceiverTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationUpdateReceiverTest.java
index f59bb8e4e..2fb465cb0 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationUpdateReceiverTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/CTLocationUpdateReceiverTest.java
@@ -2,32 +2,15 @@
import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.location.Location;
import com.google.android.gms.location.LocationResult;
import java.util.Arrays;
-import java.util.concurrent.Callable;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.mockito.invocation.*;
-import org.mockito.stubbing.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, CTGeofenceTaskManager.class})
+
public class CTLocationUpdateReceiverTest extends BaseTestCase {
@Mock
@@ -36,35 +19,18 @@ public class CTLocationUpdateReceiverTest extends BaseTestCase {
@Mock
public BroadcastReceiver.PendingResult pendingResult;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
- @Mock
- public CTGeofenceTaskManager taskManager;
-
- private Location location;
-
private LocationResult locationResult;
+ @Mock
private Logger logger;
@Before
public void setUp() throws Exception {
-
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, CTGeofenceTaskManager.class);
-
+ MockitoAnnotations.openMocks(this);
super.setUp();
-
- when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
- when(CTGeofenceAPI.getLogger()).thenReturn(logger);
-
- location = new Location("");
+ Location location = new Location("");
locationResult = LocationResult.create(Arrays.asList(new Location[]{location}));
- PowerMockito.when(CTGeofenceTaskManager.getInstance()).thenReturn(taskManager);
-
}
@Test
@@ -75,26 +41,25 @@ public void testOnReceiveWhenLastLocationNotNull() {
final Boolean[] isFinished = {false};
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- isFinished[0] = true;
- return null;
- }
+ doAnswer(invocation -> {
+ isFinished[0] = true;
+ return null;
}).when(pendingResult).finish();
Intent intent = new Intent();
intent.putExtra("com.google.android.gms.location.EXTRA_LOCATION_RESULT", locationResult);
- spy.onReceive(application, intent);
- await().until(new Callable() {
- @Override
- public Boolean call() throws Exception {
- return isFinished[0];
- }
- });
+ try (MockedStatic ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class)) {
+ ctGeofenceAPIMockedStatic.when(CTGeofenceAPI::getLogger).thenReturn(logger);
- verify(pendingResult).finish();
+ spy.onReceive(application, intent);
+
+ await().until(() -> isFinished[0]);
+
+ verify(CTGeofenceAPI.getLogger()).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG, "Location updates receiver called");
+ verify(CTGeofenceAPI.getLogger()).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG, "Returning from Location Updates Receiver");
+ verify(pendingResult).finish();
+ }
}
@Test
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GeofenceUpdateTaskTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GeofenceUpdateTaskTest.java
index 14ef82e76..613ebe33b 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GeofenceUpdateTaskTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GeofenceUpdateTaskTest.java
@@ -6,8 +6,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.verifyStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.content.Context;
import com.clevertap.android.geofence.fakes.GeofenceJSON;
@@ -18,23 +16,9 @@
import java.util.List;
import org.json.JSONObject;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.powermock.reflect.internal.WhiteboxImpl;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
import org.skyscreamer.jsonassert.JSONAssert;
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, FileUtils.class})
public class GeofenceUpdateTaskTest extends BaseTestCase {
@Mock
@@ -43,11 +27,19 @@ public class GeofenceUpdateTaskTest extends BaseTestCase {
@Mock
public CTGeofenceAdapter ctGeofenceAdapter;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
+ private MockedStatic ctGeofenceAPIMockedStatic;
+ private MockedStatic fileUtilsMockedStatic;
+
+ @Mock
private Logger logger;
+ @After
+ public void cleanup() {
+ ctGeofenceAPIMockedStatic.close();
+ fileUtilsMockedStatic.close();
+ }
+
@Test
public void executeTestTC1() throws Exception {
@@ -70,8 +62,8 @@ public void executeTestTC1() throws Exception {
ArgumentCaptor argumentCaptorJson = ArgumentCaptor.forClass(JSONObject.class);
- verifyStatic(FileUtils.class);
- FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(), argumentCaptorJson.capture());
+ fileUtilsMockedStatic.verify(() -> FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(),
+ argumentCaptorJson.capture()));
JSONAssert.assertEquals(GeofenceJSON.getFirst(), argumentCaptorJson.getValue(), true);
@@ -103,8 +95,8 @@ public void executeTestTC2() throws Exception {
ArgumentCaptor argumentCaptorJson = ArgumentCaptor.forClass(JSONObject.class);
- verifyStatic(FileUtils.class);
- FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(), argumentCaptorJson.capture());
+ fileUtilsMockedStatic.verify(() -> FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(),
+ argumentCaptorJson.capture()));
JSONAssert.assertEquals(GeofenceJSON.getEmptyGeofence(), argumentCaptorJson.getValue(), true);
@@ -136,8 +128,8 @@ public void executeTestTC3() throws Exception {
ArgumentCaptor argumentCaptorJson = ArgumentCaptor.forClass(JSONObject.class);
- verifyStatic(FileUtils.class);
- FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(), argumentCaptorJson.capture());
+ fileUtilsMockedStatic.verify(() -> FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(),
+ argumentCaptorJson.capture()));
JSONAssert.assertEquals(GeofenceJSON.getEmptyJson(), argumentCaptorJson.getValue(), true);
@@ -148,7 +140,7 @@ public void executeTestTC3() throws Exception {
}
@Test
- public void executeTestTC4() throws Exception {
+ public void executeTestTC4() {
// when old geofence is not empty and new geofence list is not empty
@@ -171,7 +163,7 @@ public void executeTestTC4() throws Exception {
verify(ctGeofenceAdapter)
.removeAllGeofence(argumentCaptorOldGeofence.capture(), any(OnSuccessListener.class));
- assertThat(argumentCaptorOldGeofence.getValue(), is(Arrays.asList(new String[]{"310001"})));
+ assertThat(argumentCaptorOldGeofence.getValue(), is(Arrays.asList("310001")));
}
@@ -197,9 +189,8 @@ public void executeTestTC5() throws Exception {
ArgumentCaptor argumentCaptorJson = ArgumentCaptor.forClass(JSONObject.class);
- verifyStatic(FileUtils.class);
- FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(), argumentCaptorJson.capture());
-
+ fileUtilsMockedStatic.verify(() -> FileUtils.writeJsonToFile(any(Context.class), anyString(), anyString(),
+ argumentCaptorJson.capture()));
JSONAssert.assertEquals(GeofenceJSON.getGeofence(), argumentCaptorJson.getValue(), true);
ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(List.class);
@@ -210,18 +201,12 @@ public void executeTestTC5() throws Exception {
@Before
public void setUp() throws Exception {
-
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, FileUtils.class);
-
+ MockitoAnnotations.openMocks(this);
super.setUp();
-
+ ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class);
+ fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class);
when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
when(CTGeofenceAPI.getLogger()).thenReturn(logger);
-
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "ctGeofenceAdapter", ctGeofenceAdapter);
-
+ when(ctGeofenceAPI.getCtGeofenceAdapter()).thenReturn(ctGeofenceAdapter);
}
-
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleGeofenceAdapterTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleGeofenceAdapterTest.java
index 819c5d1cd..d021cc42d 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleGeofenceAdapterTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleGeofenceAdapterTest.java
@@ -3,16 +3,11 @@
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.verifyStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.app.PendingIntent;
import com.clevertap.android.geofence.fakes.GeofenceJSON;
import com.clevertap.android.geofence.interfaces.CTGeofenceAdapter;
-import com.clevertap.android.geofence.interfaces.CTGeofenceTask;
import com.clevertap.android.geofence.model.CTGeofence;
-import com.clevertap.android.sdk.CleverTapAPI;
-import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationServices;
@@ -22,43 +17,18 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.ExecutionException;
-import org.hamcrest.*;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.powermock.reflect.internal.WhiteboxImpl;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, Utils.class, CleverTapAPI.class
- , LocationServices.class, Tasks.class})
-@Ignore
-public class GoogleGeofenceAdapterTest extends BaseTestCase {
- @Mock
- public CleverTapAPI cleverTapAPI;
+public class GoogleGeofenceAdapterTest extends BaseTestCase {
@Mock
public CTGeofenceAPI ctGeofenceAPI;
- @Mock
- public CTGeofenceAdapter ctLocationAdapter;
@Mock
public GeofencingClient geofencingClient;
- @Mock
- public CTGeofenceTask.OnCompleteListener onCompleteListener;
@Mock
public OnSuccessListener onSuccessListener;
@@ -66,30 +36,45 @@ public class GoogleGeofenceAdapterTest extends BaseTestCase {
@Mock
public PendingIntent pendingIntent;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
@Mock
public Task task;
+ private MockedStatic ctGeofenceAPIMockedStatic;
+
+ @Mock
+ private CTGeofenceAdapter ctGeofenceAdapter;
+
+ private MockedStatic locationServicesMockedStatic;
+
+ @Mock
private Logger logger;
+ private MockedStatic tasksMockedStatic;
+
+ private MockedStatic utilsMockedStatic;
+
+ @After
+ public void cleanup() {
+ ctGeofenceAPIMockedStatic.close();
+ locationServicesMockedStatic.close();
+ utilsMockedStatic.close();
+ tasksMockedStatic.close();
+ }
+
@Before
public void setUp() throws Exception {
-
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, Utils.class, CleverTapAPI.class,
- LocationServices.class, Tasks.class);
+ MockitoAnnotations.openMocks(this);
+ ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class);
+ locationServicesMockedStatic = Mockito.mockStatic(LocationServices.class);
+ utilsMockedStatic = Mockito.mockStatic(Utils.class);
+ tasksMockedStatic = Mockito.mockStatic(Tasks.class);
super.setUp();
when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
when(CTGeofenceAPI.getLogger()).thenReturn(logger);
-
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "ctGeofenceAdapter", ctLocationAdapter);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "cleverTapAPI", cleverTapAPI);
- PowerMockito.when(LocationServices.getGeofencingClient(application)).thenReturn(geofencingClient);
+ when(LocationServices.getGeofencingClient(application)).thenReturn(geofencingClient);
+ when(ctGeofenceAPI.getCtGeofenceAdapter()).thenReturn(ctGeofenceAdapter);
}
@@ -109,7 +94,7 @@ public void testAddAllGeofenceTC2() {
// when fence list is empty
GoogleGeofenceAdapter geofenceAdapter = new GoogleGeofenceAdapter(application);
- geofenceAdapter.addAllGeofence(new ArrayList(), onSuccessListener);
+ geofenceAdapter.addAllGeofence(new ArrayList<>(), onSuccessListener);
verify(geofencingClient, never()).addGeofences(any(GeofencingRequest.class), any(PendingIntent.class));
}
@@ -125,16 +110,8 @@ public void testAddAllGeofenceTC3() {
.thenReturn(task);
geofenceAdapter.addAllGeofence(ctGeofences, onSuccessListener);
- try {
- verifyStatic(Tasks.class);
- Tasks.await(task);
-
- verify(onSuccessListener).onSuccess(null);
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ tasksMockedStatic.verify(() -> Tasks.await(task));
+ verify(onSuccessListener).onSuccess(null);
}
@@ -144,18 +121,21 @@ public void testGetGeofencingRequest() {
List ctGeofences = CTGeofence.from(GeofenceJSON.getGeofence());
try {
- List googleGeofences = WhiteboxImpl.invokeMethod(geofenceAdapter,
- "getGoogleGeofences", ctGeofences);
- GeofencingRequest geofencingRequest = WhiteboxImpl.invokeMethod(geofenceAdapter,
- "getGeofencingRequest", googleGeofences);
-
- assertEquals(GeofencingRequest.INITIAL_TRIGGER_ENTER, geofencingRequest.getInitialTrigger());
- MatcherAssert.assertThat(geofencingRequest.getGeofences(), CoreMatchers.is(googleGeofences));
+ geofenceAdapter.addAllGeofence(ctGeofences, onSuccessListener);
+ ArgumentCaptor geofencingRequestArgumentCaptor = ArgumentCaptor.forClass(
+ GeofencingRequest.class);
+ ArgumentCaptor pendingIntentArgumentCaptor = ArgumentCaptor.forClass(PendingIntent.class);
+
+ verify(geofencingClient).addGeofences(geofencingRequestArgumentCaptor.capture(),
+ pendingIntentArgumentCaptor.capture());
+ assertEquals(GeofencingRequest.INITIAL_TRIGGER_ENTER,
+ geofencingRequestArgumentCaptor.getValue().getInitialTrigger());
} catch (Exception e) {
e.printStackTrace();
}
}
+ //
@Test
public void testRemoveAllGeofenceTC1() {
// when fence list is null
@@ -166,16 +146,18 @@ public void testRemoveAllGeofenceTC1() {
}
+ //
@Test
public void testRemoveAllGeofenceTC2() {
// when fence list is empty
GoogleGeofenceAdapter geofenceAdapter = new GoogleGeofenceAdapter(application);
- geofenceAdapter.removeAllGeofence(new ArrayList(), onSuccessListener);
+ geofenceAdapter.removeAllGeofence(new ArrayList<>(), onSuccessListener);
verify(geofencingClient, never()).removeGeofences(ArgumentMatchers.anyList());
}
+ //
@Test
public void testRemoveAllGeofenceTC3() {
// when fence list is not empty
@@ -185,16 +167,8 @@ public void testRemoveAllGeofenceTC3() {
when(geofencingClient.removeGeofences(ctGeofenceIds)).thenReturn(task);
geofenceAdapter.removeAllGeofence(ctGeofenceIds, onSuccessListener);
- try {
- verifyStatic(Tasks.class);
- Tasks.await(task);
-
- verify(onSuccessListener).onSuccess(null);
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ tasksMockedStatic.verify(() -> Tasks.await(task));
+ verify(onSuccessListener).onSuccess(null);
}
@@ -218,16 +192,8 @@ public void testStopGeofenceMonitoringTC2() {
geofenceAdapter.stopGeofenceMonitoring(pendingIntent);
- try {
- verifyStatic(Tasks.class);
- Tasks.await(task);
-
- verify(pendingIntent).cancel();
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ tasksMockedStatic.verify(() -> Tasks.await(task));
+ verify(pendingIntent).cancel();
}
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleLocationAdapterTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleLocationAdapterTest.java
index 571272e70..c55279572 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleLocationAdapterTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/GoogleLocationAdapterTest.java
@@ -8,10 +8,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.verifyStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
-
import android.app.PendingIntent;
+import android.content.Context;
import android.location.Location;
import android.util.Log;
import androidx.work.Configuration;
@@ -22,9 +20,7 @@
import androidx.work.testing.SynchronousExecutor;
import androidx.work.testing.WorkManagerTestInitHelper;
import com.clevertap.android.geofence.fakes.GeofenceEventFake;
-import com.clevertap.android.geofence.interfaces.CTGeofenceTask;
import com.clevertap.android.geofence.interfaces.CTLocationAdapter;
-import com.clevertap.android.geofence.interfaces.CTLocationCallback;
import com.clevertap.android.sdk.CleverTapAPI;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationRequest;
@@ -32,29 +28,13 @@
import com.google.android.gms.location.Priority;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
-import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.powermock.reflect.internal.WhiteboxImpl;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, Utils.class, CleverTapAPI.class
- , LocationServices.class, Tasks.class})
-@Ignore
+
+
public class GoogleLocationAdapterTest extends BaseTestCase {
@Mock
@@ -67,37 +47,38 @@ public class GoogleLocationAdapterTest extends BaseTestCase {
public CTLocationAdapter ctLocationAdapter;
@Mock
- public CTGeofenceTask.OnCompleteListener onCompleteListener;
+ public FusedLocationProviderClient providerClient;
@Mock
- public FusedLocationProviderClient providerClient;
+ private Logger logger;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
+ private MockedStatic tasksMockedStatic;
- private Logger logger;
+ private MockedStatic utilsMockedStatic;
+
+ private MockedStatic locationServicesMockedStatic;
+
+ private MockedStatic ctGeofenceAPIMockedStatic;
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, Utils.class, CleverTapAPI.class,
- LocationServices.class, Tasks.class);
+ MockitoAnnotations.openMocks(this);
- super.setUp();
+ ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class);
+ locationServicesMockedStatic = Mockito.mockStatic(LocationServices.class);
+ utilsMockedStatic = Mockito.mockStatic(Utils.class);
+ tasksMockedStatic = Mockito.mockStatic(Tasks.class);
- when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
+ when(CTGeofenceAPI.getInstance(any(Context.class))).thenReturn(ctGeofenceAPI);
when(CTGeofenceAPI.getLogger()).thenReturn(logger);
+ when(ctGeofenceAPI.getCtLocationAdapter()).thenReturn(ctLocationAdapter);
+ when(ctGeofenceAPI.getCleverTapApi()).thenReturn(cleverTapAPI);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "ctLocationAdapter", ctLocationAdapter);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "cleverTapAPI", cleverTapAPI);
+ super.setUp();
Configuration config = new Configuration.Builder()
- // Set log level to Log.DEBUG to
- // make it easier to see why tests failed
.setMinimumLoggingLevel(Log.DEBUG)
- // Use a SynchronousExecutor to make it easier to write tests
.setExecutor(new SynchronousExecutor())
.build();
@@ -106,6 +87,14 @@ public void setUp() throws Exception {
application, config);
}
+ @After
+ public void cleanup() {
+ ctGeofenceAPIMockedStatic.close();
+ locationServicesMockedStatic.close();
+ utilsMockedStatic.close();
+ tasksMockedStatic.close();
+ }
+
@Test
public void testApplySettings() {
@@ -123,23 +112,17 @@ public void testApplySettings() {
final GoogleLocationAdapter locationAdapter = new GoogleLocationAdapter(application);
try {
- WhiteboxImpl.invokeMethod(locationAdapter, "applySettings", application);
- LocationRequest actualLocationRequest = WhiteboxImpl.invokeMethod(locationAdapter,
- "getLocationRequest");
+ locationAdapter.requestLocationUpdates();
+ LocationRequest actualLocationRequest = new LocationRequest.Builder(Priority.PRIORITY_LOW_POWER,2000000)
+ .setMinUpdateIntervalMillis(2000000)
+ .setMinUpdateDistanceMeters(900)
+ .build();
assertEquals(ctGeofenceSettings.getInterval(), actualLocationRequest.getIntervalMillis());
assertEquals(ctGeofenceSettings.getFastestInterval(), actualLocationRequest.getMinUpdateIntervalMillis());
assertEquals(ctGeofenceSettings.getSmallestDisplacement(),
actualLocationRequest.getMinUpdateDistanceMeters(), 0);
assertEquals(Priority.PRIORITY_LOW_POWER, actualLocationRequest.getPriority());
-
- Field actualFetchMode = WhiteboxImpl.getField(GoogleLocationAdapter.class, "locationFetchMode");
- Field actualLocationUpdateEnabled = WhiteboxImpl.getField(GoogleLocationAdapter.class,
- "backgroundLocationUpdatesEnabled");
-
- assertEquals(ctGeofenceSettings.getLocationFetchMode(), actualFetchMode.getInt(locationAdapter));
- assertEquals(ctGeofenceSettings.isBackgroundLocationUpdatesEnabled(),
- actualLocationUpdateEnabled.getBoolean(locationAdapter));
} catch (Exception e) {
e.printStackTrace();
}
@@ -153,26 +136,19 @@ public void testGetLastLocation() {
.thenReturn(providerClient);
Task locationTask = mock(Task.class);
try {
- PowerMockito.when(Tasks.await(locationTask)).thenReturn(expectedLocation);
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
+ when(Tasks.await(locationTask)).thenReturn(expectedLocation);
+ } catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
when(providerClient.getLastLocation()).thenReturn(locationTask);
final GoogleLocationAdapter locationAdapter = new GoogleLocationAdapter(application);
- locationAdapter.getLastLocation(new CTLocationCallback() {
- @Override
- public void onLocationComplete(Location location) {
- Assert.assertSame(expectedLocation, location);
- }
- });
+ locationAdapter.getLastLocation(location -> Assert.assertSame(expectedLocation, location));
}
@Test
- public void testRequestLocationUpdatesTC1() throws Exception {
+ public void testRequestLocationUpdatesTC1() {
// when backgroundLocationUpdates not enabled
@@ -233,13 +209,11 @@ public void testRequestLocationUpdatesTC2() throws Exception {
verify(providerClient).requestLocationUpdates(
any(LocationRequest.class), any(PendingIntent.class));
- verifyStatic(Tasks.class);
- Tasks.await(null);
+ tasksMockedStatic.verify(() -> Tasks.await(null));
}
@Test
- @Ignore
public void testRequestLocationUpdatesTC3() throws Exception {
// when backgroundLocationUpdates is enabled and fetch mode is last location
@@ -274,8 +248,7 @@ public void testRequestLocationUpdatesTC3() throws Exception {
verify(providerClient).removeLocationUpdates(any(PendingIntent.class));
- verifyStatic(Tasks.class);
- Tasks.await(null);
+ tasksMockedStatic.verify(() -> Tasks.await(null));
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/LocationUpdateTaskTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/LocationUpdateTaskTest.java
index 824a214a6..4273e73ed 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/LocationUpdateTaskTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/LocationUpdateTaskTest.java
@@ -1,32 +1,14 @@
package com.clevertap.android.geofence;
-import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.verifyStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
-
import android.app.PendingIntent;
import android.content.Context;
import com.clevertap.android.geofence.interfaces.CTGeofenceTask;
import com.clevertap.android.geofence.interfaces.CTLocationAdapter;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.powermock.reflect.internal.WhiteboxImpl;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, Utils.class, FileUtils.class})
+
public class LocationUpdateTaskTest extends BaseTestCase {
@Mock
@@ -38,39 +20,60 @@ public class LocationUpdateTaskTest extends BaseTestCase {
@Mock
public CTGeofenceTask.OnCompleteListener onCompleteListener;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
+ @Mock
private Logger logger;
+ @Mock
+ PendingIntent pendingIntent;
+
+ private MockedStatic ctGeofenceAPIMockedStatic;
+
+ private MockedStatic fileUtilsMockedStatic;
+ private MockedStatic utilsMockedStatic;
+
+ private MockedStatic pendingIntentFactoryMockedStatic;
+
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, Utils.class, FileUtils.class);
+ MockitoAnnotations.openMocks(this);
+ ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class);
+ fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class);
+ utilsMockedStatic = Mockito.mockStatic(Utils.class);
+ pendingIntentFactoryMockedStatic = Mockito.mockStatic(PendingIntentFactory.class);
super.setUp();
when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
when(CTGeofenceAPI.getLogger()).thenReturn(logger);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "ctLocationAdapter", ctLocationAdapter);
+ when(ctGeofenceAPI.getCtLocationAdapter()).thenReturn(ctLocationAdapter);
+
+ }
+ @After
+ public void cleanup() {
+ ctGeofenceAPIMockedStatic.close();
+ fileUtilsMockedStatic.close();
+ utilsMockedStatic.close();
+ pendingIntentFactoryMockedStatic.close();
}
@Test
public void testExecuteTC1() {
// when pending intent is null and bgLocationUpdate is true
+ CTGeofenceSettings ctGeofenceSettings = new CTGeofenceSettings.Builder()
+ .enableBackgroundLocationUpdates(true).build();
+ when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(ctGeofenceSettings);
+
LocationUpdateTask task = new LocationUpdateTask(application);
task.setOnCompleteListener(onCompleteListener);
task.execute();
verify(ctLocationAdapter).requestLocationUpdates();
- verifyStatic(Utils.class);
- Utils.writeSettingsToFile(any(Context.class), any(CTGeofenceSettings.class));
+ utilsMockedStatic.verify(() -> Utils.writeSettingsToFile(any(Context.class), any(CTGeofenceSettings.class)));
verify(onCompleteListener).onComplete();
}
@@ -90,8 +93,7 @@ public void testExecuteTC2() {
verify(ctLocationAdapter, never()).requestLocationUpdates();
verify(ctLocationAdapter, never()).removeLocationUpdates(any(PendingIntent.class));
- verifyStatic(Utils.class);
- Utils.writeSettingsToFile(any(Context.class), any(CTGeofenceSettings.class));
+ utilsMockedStatic.verify(() -> Utils.writeSettingsToFile(any(Context.class), any(CTGeofenceSettings.class)));
}
@@ -105,8 +107,7 @@ public void testExecuteTC3() {
when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(ctGeofenceSettings);
// make pending intent non-null
- PendingIntent pendingIntent = PendingIntentFactory.getPendingIntent(application,
- PendingIntentFactory.PENDING_INTENT_LOCATION, PendingIntent.FLAG_UPDATE_CURRENT);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
LocationUpdateTask task = new LocationUpdateTask(application);
task.execute();
@@ -115,8 +116,7 @@ public void testExecuteTC3() {
verify(ctLocationAdapter).removeLocationUpdates(any(PendingIntent.class));
verify(ctLocationAdapter, never()).requestLocationUpdates();
- verifyStatic(Utils.class);
- Utils.writeSettingsToFile(any(Context.class), any(CTGeofenceSettings.class));
+ utilsMockedStatic.verify(() -> Utils.writeSettingsToFile(any(Context.class), any(CTGeofenceSettings.class)));
}
@@ -124,17 +124,16 @@ public void testExecuteTC3() {
public void testExecuteWhenLocationAdapterIsNull() {
// when location adapter is null
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "ctLocationAdapter", (Object[]) null);
+ when(ctGeofenceAPI.getCtLocationAdapter()).thenReturn(null);
LocationUpdateTask task = new LocationUpdateTask(application);
task.execute();
- verifyStatic(Utils.class, never());
- Utils.writeSettingsToFile(any(Context.class), any(CTGeofenceSettings.class));
+ utilsMockedStatic.verifyNoInteractions();
}
@Test
- public void testIsRequestLocationTC1() throws Exception {
+ public void testIsRequestLocationTC1() {
// when currentBgLocationUpdate is false
//
// CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -144,8 +143,6 @@ public void testIsRequestLocationTC1() throws Exception {
//
// CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder().build();
//
-// PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
-//
// LocationUpdateTask task = new LocationUpdateTask(application);
// boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation", null);
//
@@ -154,7 +151,7 @@ public void testIsRequestLocationTC1() throws Exception {
}
@Test
- public void testIsRequestLocationTC10() throws Exception {
+ public void testIsRequestLocationTC10() {
// when currentBgLocationUpdate is true and there is no change in settings
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -162,42 +159,44 @@ public void testIsRequestLocationTC10() throws Exception {
.build();
when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(currentGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder()
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
+ task.execute();
- assertFalse(isRequestLocation);
+ verify(logger).verbose(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "Dropping duplicate location update request");
}
@Test
- public void testIsRequestLocationTC2() throws Exception {
+ public void testIsRequestLocationTC2() {
// when currentBgLocationUpdate is true and pendingIntent is null
-// CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
-// .enableBackgroundLocationUpdates(true).build();
-//
-// when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(currentGeofenceSettings);
-//
-// CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder().build();
-//
-// PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
-//
-// LocationUpdateTask task = new LocationUpdateTask(application);
-// boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation", null);
-//
-// assertTrue(isRequestLocation);
+ CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
+ .enableBackgroundLocationUpdates(true).build();
+
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(null);
+ when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(currentGeofenceSettings);
+
+ CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder().build();
+
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+
+ LocationUpdateTask task = new LocationUpdateTask(application);
+ task.execute();
+
+ verify(ctLocationAdapter).requestLocationUpdates();
}
@Test
- public void testIsRequestLocationTC3() throws Exception {
+ public void testIsRequestLocationTC3() {
// when fetch mode is current and change in accuracy
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -207,23 +206,23 @@ public void testIsRequestLocationTC3() throws Exception {
.build();
when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(currentGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
+
CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder()
.setLocationAccuracy(CTGeofenceSettings.ACCURACY_LOW)
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
-
- assertTrue(isRequestLocation);
+ task.execute();
+ verify(ctLocationAdapter).requestLocationUpdates();
}
@Test
- public void testIsRequestLocationTC4() throws Exception {
+ public void testIsRequestLocationTC4() {
// when fetch mode is current and change in interval
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -233,23 +232,23 @@ public void testIsRequestLocationTC4() throws Exception {
.build();
when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(currentGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
+
CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder()
.setInterval(5000000)
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
-
- assertTrue(isRequestLocation);
+ task.execute();
+ verify(ctLocationAdapter).requestLocationUpdates();
}
@Test
- public void testIsRequestLocationTC5() throws Exception {
+ public void testIsRequestLocationTC5() {
// when fetch mode is current and change in fastest interval
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -259,23 +258,23 @@ public void testIsRequestLocationTC5() throws Exception {
.build();
when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(currentGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder()
.setFastestInterval(5000000)
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
+ task.execute();
- assertTrue(isRequestLocation);
+ verify(ctLocationAdapter).requestLocationUpdates();
}
@Test
- public void testIsRequestLocationTC6() throws Exception {
+ public void testIsRequestLocationTC6() {
// when fetch mode is current and change in displacement
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -285,23 +284,23 @@ public void testIsRequestLocationTC6() throws Exception {
.build();
when(ctGeofenceAPI.getGeofenceSettings()).thenReturn(currentGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
+
CTGeofenceSettings lastGeofenceSettings = new CTGeofenceSettings.Builder()
.setSmallestDisplacement(700)
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
-
- assertTrue(isRequestLocation);
+ task.execute();
+ verify(ctLocationAdapter).requestLocationUpdates();
}
@Test
- public void testIsRequestLocationTC7() throws Exception {
+ public void testIsRequestLocationTC7() {
// when fetch mode is last location and change in interval
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -316,18 +315,17 @@ public void testIsRequestLocationTC7() throws Exception {
.setInterval(5000000)
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
-
- assertTrue(isRequestLocation);
+ task.execute();
+ verify(ctLocationAdapter).requestLocationUpdates();
}
@Test
- public void testIsRequestLocationTC8() throws Exception {
+ public void testIsRequestLocationTC8() {
// when fetch mode is current location and change in fetch mode
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -341,18 +339,17 @@ public void testIsRequestLocationTC8() throws Exception {
.setLocationFetchMode(CTGeofenceSettings.FETCH_LAST_LOCATION_PERIODIC)
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
-
- assertTrue(isRequestLocation);
+ task.execute();
+ verify(ctLocationAdapter).requestLocationUpdates();
}
@Test
- public void testIsRequestLocationTC9() throws Exception {
+ public void testIsRequestLocationTC9() {
// when fetch mode is last location and change in fetch mode
CTGeofenceSettings currentGeofenceSettings = new CTGeofenceSettings.Builder()
@@ -366,14 +363,14 @@ public void testIsRequestLocationTC9() throws Exception {
.setLocationFetchMode(CTGeofenceSettings.FETCH_CURRENT_LOCATION_PERIODIC)
.build();
- PowerMockito.when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(Utils.readSettingsFromFile(application)).thenReturn(lastGeofenceSettings);
+ when(PendingIntentFactory.getPendingIntent(any(Context.class),any(int.class),any(int.class))).thenReturn(pendingIntent);
- LocationUpdateTask task = new LocationUpdateTask(application);
- boolean isRequestLocation = WhiteboxImpl.invokeMethod(task, "isRequestLocation",
- mock(PendingIntent.class));
- assertTrue(isRequestLocation);
+ LocationUpdateTask task = new LocationUpdateTask(application);
+ task.execute();
+ verify(ctLocationAdapter).requestLocationUpdates();
}
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PendingIntentFactoryTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PendingIntentFactoryTest.java
index 8a1478884..699191072 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PendingIntentFactoryTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PendingIntentFactoryTest.java
@@ -4,32 +4,15 @@
import android.app.PendingIntent;
import android.content.ComponentName;
-import com.clevertap.android.sdk.CleverTapAPI;
import org.junit.*;
-import org.junit.runner.*;
-import org.mockito.*;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
-import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPendingIntent;
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, CleverTapAPI.class})
public class PendingIntentFactoryTest extends BaseTestCase {
-
@Before
public void setUp() throws Exception {
-
- MockitoAnnotations.initMocks(this);
super.setUp();
-
}
@Test
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushGeofenceEventTaskTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushGeofenceEventTaskTest.java
index b2e903984..5119163ad 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushGeofenceEventTaskTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushGeofenceEventTaskTest.java
@@ -3,8 +3,6 @@
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.verifyStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.content.Context;
import android.content.Intent;
@@ -20,27 +18,11 @@
import java.util.concurrent.Future;
import org.json.JSONObject;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.powermock.reflect.internal.WhiteboxImpl;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
import org.skyscreamer.jsonassert.JSONAssert;
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, CleverTapAPI.class, Utils.class, GeofencingEvent.class
- , FileUtils.class})
-@Ignore
-public class PushGeofenceEventTaskTest extends BaseTestCase {
+public class PushGeofenceEventTaskTest extends BaseTestCase {
@Mock
public CleverTapAPI cleverTapAPI;
@@ -51,9 +33,6 @@ public class PushGeofenceEventTaskTest extends BaseTestCase {
@Mock
public CTGeofenceTask.OnCompleteListener onCompleteListener;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
@Mock
private GeofencingEvent geofencingEvent;
@@ -61,25 +40,46 @@ public class PushGeofenceEventTaskTest extends BaseTestCase {
private Location location;
+ @Mock
private Logger logger;
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, Utils.class, CleverTapAPI.class
- , GeofencingEvent.class, FileUtils.class);
- super.setUp();
+ private MockedStatic utilsMockedStatic;
- when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
- when(CTGeofenceAPI.getLogger()).thenReturn(logger);
+ private MockedStatic fileUtilsMockedStatic;
+
+ private MockedStatic geofencingEventMockedStatic;
+ private MockedStatic ctGeofenceAPIMockedStatic;
+
+ @Before
+ public void setUp() throws Exception {
+
+ MockitoAnnotations.openMocks(this);
location = new Location("");
intent = new Intent();
+
+ ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class);
+ utilsMockedStatic = Mockito.mockStatic(Utils.class);
+ fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class);
+ geofencingEventMockedStatic = Mockito.mockStatic(GeofencingEvent.class);
+
+ when(CTGeofenceAPI.getInstance(any(Context.class))).thenReturn(ctGeofenceAPI);
+ when(CTGeofenceAPI.getLogger()).thenReturn(logger);
when(GeofencingEvent.fromIntent(intent)).thenReturn(geofencingEvent);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "cleverTapAPI", cleverTapAPI);
+ when(ctGeofenceAPI.getCleverTapApi()).thenReturn(cleverTapAPI);
+
+ super.setUp();
+
+ }
+
+ @After
+ public void cleanup() {
+ ctGeofenceAPIMockedStatic.close();
+ fileUtilsMockedStatic.close();
+ utilsMockedStatic.close();
+ geofencingEventMockedStatic.close();
}
@Test
@@ -96,9 +96,7 @@ public void testExecuteWhenCleverTapApiIsNull() {
task.setOnCompleteListener(onCompleteListener);
task.execute();
- verifyStatic(GeofencingEvent.class, times(0));
- GeofencingEvent.fromIntent(intent);
-
+ geofencingEventMockedStatic.verify(() -> GeofencingEvent.fromIntent(intent), times(0));
verify(onCompleteListener).onComplete();
}
@@ -179,8 +177,7 @@ public void testExecuteWhenTriggeredGeofenceNotNull() {
task.setOnCompleteListener(onCompleteListener);
task.execute();
- verifyStatic(FileUtils.class);
- FileUtils.readFromFile(any(Context.class), anyString());
+ fileUtilsMockedStatic.verify(() -> FileUtils.readFromFile(any(Context.class), anyString()));
verify(onCompleteListener).onComplete();
@@ -190,24 +187,25 @@ public void testExecuteWhenTriggeredGeofenceNotNull() {
public void testPushGeofenceEventsWhenEnter() {
// When old geofence in file is not empty and triggered geofence found in file
+ Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
Future future = Mockito.mock(Future.class);
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences())
+ .thenReturn(GeofenceEventFake.getSingleMatchingTriggeredGeofenceList());
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(triggeredLocation);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(1);
+
when(FileUtils.getCachedFullPath(any(Context.class), anyString())).thenReturn("");
when(FileUtils.readFromFile(any(Context.class),
anyString())).thenReturn(GeofenceJSON.getGeofenceString());
when(cleverTapAPI.pushGeofenceEnteredEvent(any(JSONObject.class))).thenReturn(future);
- List triggeredGeofenceList = GeofenceEventFake.getSingleMatchingTriggeredGeofenceList();
- Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
-
+ task.execute();
try {
- // Geofence Entered event
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents",
- triggeredGeofenceList,
- triggeredLocation, Geofence.GEOFENCE_TRANSITION_ENTER);
-
JSONObject firstFromGeofenceArray = GeofenceJSON.getFirstFromGeofenceArray().getJSONObject(0);
firstFromGeofenceArray.put("triggered_lat", triggeredLocation.getLatitude());
firstFromGeofenceArray.put("triggered_lng", triggeredLocation.getLongitude());
@@ -231,22 +229,23 @@ public void testPushGeofenceEventsWhenExit() {
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
Future future = Mockito.mock(Future.class);
+ Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
+
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences())
+ .thenReturn(GeofenceEventFake.getSingleMatchingTriggeredGeofenceList());
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(triggeredLocation);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(2);
when(FileUtils.getCachedFullPath(any(Context.class), anyString())).thenReturn("");
when(FileUtils.readFromFile(any(Context.class),
anyString())).thenReturn(GeofenceJSON.getGeofenceString());
when(cleverTapAPI.pushGeoFenceExitedEvent(any(JSONObject.class))).thenReturn(future);
- List triggeredGeofenceList = GeofenceEventFake.getSingleMatchingTriggeredGeofenceList();
- Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
-
+ task.execute();
try {
- // Geofence Exit event
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents",
- triggeredGeofenceList,
- triggeredLocation, Geofence.GEOFENCE_TRANSITION_EXIT);
-
JSONObject firstFromGeofenceArray = GeofenceJSON.getFirstFromGeofenceArray().getJSONObject(0);
firstFromGeofenceArray.put("triggered_lat", triggeredLocation.getLatitude());
firstFromGeofenceArray.put("triggered_lng", triggeredLocation.getLongitude());
@@ -269,22 +268,24 @@ public void testPushGeofenceEventsWhenMultipleExit() {
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
Future future = Mockito.mock(Future.class);
+ Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
+
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences())
+ .thenReturn(GeofenceEventFake.getDoubleMatchingTriggeredGeofenceList());
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(triggeredLocation);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(2);
when(FileUtils.getCachedFullPath(any(Context.class), anyString())).thenReturn("");
when(FileUtils.readFromFile(any(Context.class),
anyString())).thenReturn(GeofenceJSON.getGeofenceString());
when(cleverTapAPI.pushGeoFenceExitedEvent(any(JSONObject.class))).thenReturn(future);
-
- List triggeredGeofenceList = GeofenceEventFake.getDoubleMatchingTriggeredGeofenceList();
- Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
+ task.execute();
try {
// Multiple Geofence Exit event
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents",
- triggeredGeofenceList,
- triggeredLocation, Geofence.GEOFENCE_TRANSITION_EXIT);
-
JSONObject firstFromGeofenceArray = GeofenceJSON.getFirstFromGeofenceArray().getJSONObject(0);
firstFromGeofenceArray.put("triggered_lat", triggeredLocation.getLatitude());
firstFromGeofenceArray.put("triggered_lng", triggeredLocation.getLongitude());
@@ -315,20 +316,21 @@ public void testPushGeofenceEventsWhenOldGeofenceIsEmpty() {
// When old geofence in file is empty
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
+ Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
+
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences())
+ .thenReturn(GeofenceEventFake.getSingleMatchingTriggeredGeofenceList());
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(triggeredLocation);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(2);
when(FileUtils.getCachedFullPath(any(Context.class), anyString())).thenReturn("");
when(FileUtils.readFromFile(any(Context.class),
anyString())).thenReturn("");
- List triggeredGeofenceList = GeofenceEventFake.getSingleMatchingTriggeredGeofenceList();
- Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
-
+ task.execute();
try {
-
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents",
- triggeredGeofenceList,
- triggeredLocation, Geofence.GEOFENCE_TRANSITION_ENTER);
-
verify(cleverTapAPI, never()).pushGeofenceEnteredEvent(any(JSONObject.class));
} catch (Exception e) {
e.printStackTrace();
@@ -341,20 +343,21 @@ public void testPushGeofenceEventsWhenOldGeofenceJsonArrayIsEmpty() {
// When old geofence json array in file is empty
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
+ Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
+
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences())
+ .thenReturn(GeofenceEventFake.getSingleMatchingTriggeredGeofenceList());
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(triggeredLocation);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(2);
when(FileUtils.getCachedFullPath(any(Context.class), anyString())).thenReturn("");
when(FileUtils.readFromFile(any(Context.class),
anyString())).thenReturn(GeofenceJSON.getEmptyGeofence().toString());
- List triggeredGeofenceList = GeofenceEventFake.getSingleMatchingTriggeredGeofenceList();
- Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
-
+ task.execute();
try {
-
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents",
- triggeredGeofenceList,
- triggeredLocation, Geofence.GEOFENCE_TRANSITION_ENTER);
-
verify(cleverTapAPI, never()).pushGeofenceEnteredEvent(any(JSONObject.class));
} catch (Exception e) {
e.printStackTrace();
@@ -362,25 +365,27 @@ public void testPushGeofenceEventsWhenOldGeofenceJsonArrayIsEmpty() {
}
+ //
@Test
public void testPushGeofenceEventsWhenOldGeofenceJsonInvalid() {
// When old geofence json content in file is invalid
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
+ Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
+
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences())
+ .thenReturn(GeofenceEventFake.getSingleMatchingTriggeredGeofenceList());
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(triggeredLocation);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(2);
when(FileUtils.getCachedFullPath(any(Context.class), anyString())).thenReturn("");
when(FileUtils.readFromFile(any(Context.class),
anyString())).thenReturn(GeofenceJSON.getEmptyJson().toString());
- List triggeredGeofenceList = GeofenceEventFake.getSingleMatchingTriggeredGeofenceList();
- Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
-
+ task.execute();
try {
-
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents",
- triggeredGeofenceList,
- triggeredLocation, Geofence.GEOFENCE_TRANSITION_ENTER);
-
verify(cleverTapAPI, never()).pushGeofenceEnteredEvent(any(JSONObject.class));
} catch (Exception e) {
e.printStackTrace();
@@ -393,20 +398,21 @@ public void testPushGeofenceEventsWhenTriggeredGeofenceIsNotFound() {
// When triggered geofence not found in file
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
+ Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
+
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences())
+ .thenReturn(GeofenceEventFake.getNonMatchingTriggeredGeofenceList());
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(triggeredLocation);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(2);
when(FileUtils.getCachedFullPath(any(Context.class), anyString())).thenReturn("");
when(FileUtils.readFromFile(any(Context.class),
anyString())).thenReturn(GeofenceJSON.getGeofenceString());
- List triggeredGeofenceList = GeofenceEventFake.getNonMatchingTriggeredGeofenceList();
- Location triggeredLocation = GeofenceEventFake.getTriggeredLocation();
-
+ task.execute();
try {
-
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents",
- triggeredGeofenceList,
- triggeredLocation, Geofence.GEOFENCE_TRANSITION_ENTER);
-
verify(cleverTapAPI, never()).pushGeofenceEnteredEvent(any(JSONObject.class));
verify(cleverTapAPI).pushGeoFenceError(anyInt(), anyString());
@@ -421,19 +427,16 @@ public void testPushGeofenceEventsWhenTriggeredGeofenceIsNull() {
// When triggered geofence is null
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+ Mockito.when(geofencingEvent.hasError()).thenReturn(false);
+ Mockito.when(geofencingEvent.getTriggeringGeofences()).thenReturn(null);
+ Mockito.when(geofencingEvent.getTriggeringLocation()).thenReturn(location);
+ when(geofencingEvent.getGeofenceTransition()).thenReturn(2);
- try {
- WhiteboxImpl.invokeMethod(task, "pushGeofenceEvents", (Object) null,
- null, 1);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
+ task.execute();
verify(cleverTapAPI).pushGeoFenceError(anyInt(), anyString());
- verifyStatic(FileUtils.class, times(0));
- FileUtils.readFromFile(any(Context.class), anyString());
-
+ fileUtilsMockedStatic.verify(() -> FileUtils.readFromFile(any(Context.class), anyString()), times(0));
}
@Test
@@ -442,26 +445,18 @@ public void testSendOnCompleteEventWhenListenerIsNull() {
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
task.setOnCompleteListener(null);
- try {
- WhiteboxImpl.invokeMethod(task, "sendOnCompleteEvent");
- } catch (Exception e) {
- e.printStackTrace();
- }
+ task.execute();
Mockito.verify(onCompleteListener, times(0)).onComplete();
}
-
@Test
public void testSendOnCompleteEventWhenListenerNotNull() {
// when listener not null
PushGeofenceEventTask task = new PushGeofenceEventTask(application, intent);
task.setOnCompleteListener(onCompleteListener);
- try {
- WhiteboxImpl.invokeMethod(task, "sendOnCompleteEvent");
- } catch (Exception e) {
- e.printStackTrace();
- }
+ task.execute();
+
Mockito.verify(onCompleteListener).onComplete();
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushLocationEventTaskTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushLocationEventTaskTest.java
index f7b9a997a..0ae1e1747 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushLocationEventTaskTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/PushLocationEventTaskTest.java
@@ -2,8 +2,6 @@
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
-import static org.powermock.api.mockito.PowerMockito.verifyStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
import android.content.Context;
import android.location.Location;
@@ -14,23 +12,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.junit.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.powermock.reflect.internal.WhiteboxImpl;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({CTGeofenceAPI.class, CleverTapAPI.class, Utils.class, LocationResult.class})
-@Ignore
+
public class PushLocationEventTaskTest extends BaseTestCase {
@Mock
@@ -42,29 +25,32 @@ public class PushLocationEventTaskTest extends BaseTestCase {
@Mock
public CTGeofenceTask.OnCompleteListener onCompleteListener;
- @Rule
- public PowerMockRule rule = new PowerMockRule();
-
- private Location location;
+ private MockedStatic ctGeofenceAPIMockedStatic;
private LocationResult locationResult;
+ @Mock
private Logger logger;
+ private MockedStatic utilsMockedStatic;
+
+ @After
+ public void cleanup() {
+ ctGeofenceAPIMockedStatic.close();
+ utilsMockedStatic.close();
+ }
+
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class, Utils.class, CleverTapAPI.class);
+ MockitoAnnotations.openMocks(this);
super.setUp();
-
+ Location location = new Location("");
+ locationResult = LocationResult.create(Arrays.asList(new Location[]{location}));
+ ctGeofenceAPIMockedStatic = mockStatic(CTGeofenceAPI.class);
+ utilsMockedStatic = mockStatic(Utils.class);
when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
when(CTGeofenceAPI.getLogger()).thenReturn(logger);
-
- location = new Location("");
- locationResult = LocationResult.create(Arrays.asList(new Location[]{location}));
-
}
@Test
@@ -81,15 +67,13 @@ public void testExecuteWhenCleverTapApiIsNull() {
task.setOnCompleteListener(onCompleteListener);
task.execute();
- verifyStatic(Utils.class, times(0));
- Utils.notifyLocationUpdates(any(Context.class), any(Location.class));
-
+ utilsMockedStatic.verify(() -> Utils.notifyLocationUpdates(any(Context.class), any(Location.class)),
+ times(0));
Mockito.verify(onCompleteListener).onComplete();
-
}
@Test
- public void testExecuteWhenCleverTapApiNotNullAndFutureIsNull() {
+ public void testExecuteWhenCleverTapApiNotNullAndFutureIsNullAndListenerNotNull() {
PushLocationEventTask task = new PushLocationEventTask(application, locationResult);
@@ -97,23 +81,38 @@ public void testExecuteWhenCleverTapApiNotNullAndFutureIsNull() {
task.setOnCompleteListener(onCompleteListener);
when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "cleverTapAPI", cleverTapAPI);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "context", application);
-
Mockito.when(cleverTapAPI.setLocationForGeofences(any(Location.class), anyInt())).
thenReturn(null);
task.execute();
- verifyStatic(Utils.class);
- Utils.notifyLocationUpdates(any(Context.class), any(Location.class));
- Mockito.verify(cleverTapAPI).setLocationForGeofences(any(Location.class), anyInt());
+ utilsMockedStatic.verify(() -> Utils.notifyLocationUpdates(any(Context.class), any(Location.class)));
+ Mockito.verify(logger).verbose(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "Dropping location ping event to CT server");
Mockito.verify(onCompleteListener).onComplete();
+ }
+
+ @Test
+ public void testExecuteWhenCleverTapApiNotNullAndFutureIsNullAndListenerNull() {
+
+ PushLocationEventTask task = new PushLocationEventTask(application, locationResult);
+
+ // when listener null
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
+
+ Mockito.when(cleverTapAPI.setLocationForGeofences(any(Location.class), anyInt())).
+ thenReturn(null);
+
+ task.execute();
+ utilsMockedStatic.verify(() -> Utils.notifyLocationUpdates(any(Context.class), any(Location.class)));
+ Mockito.verify(logger).verbose(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "Dropping location ping event to CT server");
+ Mockito.verifyNoMoreInteractions(onCompleteListener);
}
@Test
- public void testExecuteWhenCleverTapApiNotNullAndFutureNotNull() {
+ public void testExecuteWhenCleverTapApiNotNullAndFutureNotNullAndListenerNotNull() {
Future future = Mockito.mock(Future.class);
PushLocationEventTask task = new PushLocationEventTask(application, locationResult);
@@ -122,56 +121,44 @@ public void testExecuteWhenCleverTapApiNotNullAndFutureNotNull() {
task.setOnCompleteListener(onCompleteListener);
when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "cleverTapAPI", cleverTapAPI);
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "context", application);
-
- Mockito.when(cleverTapAPI.setLocationForGeofences(any(Location.class), anyInt())).
+ Mockito.when(ctGeofenceAPI.processTriggeredLocation(any(Location.class))).
thenReturn(future);
task.execute();
- verifyStatic(Utils.class);
- Utils.notifyLocationUpdates(any(Context.class), any(Location.class));
-
+ utilsMockedStatic.verify(() -> Utils.notifyLocationUpdates(any(Context.class), any(Location.class)));
try {
Mockito.verify(future).get();
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
+ } catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
+ Mockito.verify(logger).verbose(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "Calling future for setLocationForGeofences()");
Mockito.verify(onCompleteListener).onComplete();
-
}
@Test
- public void testSendOnCompleteEventWhenListenerIsNull() {
- // when listener is null
- PushLocationEventTask task = new PushLocationEventTask(application, locationResult);
+ public void testExecuteWhenCleverTapApiNotNullAndFutureNotNullAndListenerNull() {
+ Future future = Mockito.mock(Future.class);
- task.setOnCompleteListener(null);
- try {
- WhiteboxImpl.invokeMethod(task, "sendOnCompleteEvent");
- } catch (Exception e) {
- e.printStackTrace();
- }
- Mockito.verify(onCompleteListener, times(0)).onComplete();
+ PushLocationEventTask task = new PushLocationEventTask(application, locationResult);
- }
+ // when listener null
+ when(Utils.initCTGeofenceApiIfRequired(application)).thenReturn(true);
- @Test
- public void testSendOnCompleteEventWhenListenerNotNull() {
- // when listener not null
- PushLocationEventTask task = new PushLocationEventTask(application, locationResult);
+ Mockito.when(ctGeofenceAPI.processTriggeredLocation(any(Location.class))).
+ thenReturn(future);
+ task.execute();
- task.setOnCompleteListener(onCompleteListener);
+ utilsMockedStatic.verify(() -> Utils.notifyLocationUpdates(any(Context.class), any(Location.class)));
try {
- WhiteboxImpl.invokeMethod(task, "sendOnCompleteEvent");
- } catch (Exception e) {
+ Mockito.verify(future).get();
+ } catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
- Mockito.verify(onCompleteListener).onComplete();
+ Mockito.verify(logger).verbose(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "Calling future for setLocationForGeofences()");
+ Mockito.verifyNoMoreInteractions(onCompleteListener);
}
-
}
diff --git a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/UtilsTest.java b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/UtilsTest.java
index 27851c88a..fe5af9b88 100644
--- a/clevertap-geofence/src/test/java/com/clevertap/android/geofence/UtilsTest.java
+++ b/clevertap-geofence/src/test/java/com/clevertap/android/geofence/UtilsTest.java
@@ -4,9 +4,7 @@
import static org.hamcrest.beans.SamePropertyValuesAs.*;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.verifyStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.when;
import android.Manifest;
import android.content.Context;
@@ -24,53 +22,42 @@
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.*;
-import org.junit.function.*;
-import org.junit.runner.*;
import org.mockito.*;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.powermock.reflect.Whitebox;
-import org.powermock.reflect.internal.WhiteboxImpl;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
-import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
import org.skyscreamer.jsonassert.JSONAssert;
-@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 28,
- application = TestApplication.class
-)
-@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*", "org.json.*"})
-@PrepareForTest({FileUtils.class, CTGeofenceAPI.class, CleverTapAPI.class,
- com.clevertap.android.sdk.Utils.class})
public class UtilsTest extends BaseTestCase {
- @Rule
- public PowerMockRule rule = new PowerMockRule();
+ @Mock
+ private CleverTapAPI cleverTapAPI;
+ @Mock
private CTGeofenceAPI ctGeofenceAPI;
+ private MockedStatic ctGeofenceAPIMockedStatic;
+
+ @Mock
private Logger logger;
- /* @Mock
- private static Logger logger;*/
private ShadowApplication shadowApplication;
+ @After
+ public void cleanup() {
+ ctGeofenceAPIMockedStatic.close();
+ }
+
@Before
public void setUp() throws Exception {
- //MockitoAnnotations.initMocks(this);
- PowerMockito.mockStatic(CTGeofenceAPI.class);
super.setUp();
+ MockitoAnnotations.openMocks(this);
+
shadowApplication = Shadows.shadowOf(application);
- ctGeofenceAPI = Mockito.mock(CTGeofenceAPI.class);
+ ctGeofenceAPIMockedStatic = Mockito.mockStatic(CTGeofenceAPI.class);
when(CTGeofenceAPI.getInstance(application)).thenReturn(ctGeofenceAPI);
- logger = new Logger(Logger.DEBUG);
when(CTGeofenceAPI.getLogger()).thenReturn(logger);
-
}
@Test
@@ -86,15 +73,15 @@ public void testEmptyIfNull() {
@Test
public void testHasBackgroundLocationPermission() {
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.P);
// when SDK Level is less than Q
- Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.P);
boolean actualWhenSdkIsP = Utils.hasBackgroundLocationPermission(application);
assertTrue("hasBackgroundLocationPermission must return true when sdk int is less than Q", actualWhenSdkIsP);
// when SDK Level is greater than P and permission denied
- Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.Q);
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.Q);
shadowApplication.denyPermissions(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
boolean actualWhenPermissionDenied = Utils.hasBackgroundLocationPermission(application);
@@ -105,7 +92,6 @@ public void testHasBackgroundLocationPermission() {
@Test
public void testHasPermission() {
- //mockStatic(ContextCompat.class);
ShadowApplication shadowApplication = Shadows.shadowOf(application);
@@ -122,7 +108,6 @@ public void testHasPermission() {
assertTrue("hasPermission must return true when permission is granted", actualWhenPermissionGranted);
// when permission not null and checkSelfPermission returns permission denied
-
shadowApplication.denyPermissions(Manifest.permission.ACCESS_FINE_LOCATION);
boolean actualWhenPermissionDenied = Utils
.hasPermission(application, Manifest.permission.ACCESS_FINE_LOCATION);
@@ -131,43 +116,42 @@ public void testHasPermission() {
@Test
public void testInitCTGeofenceApiIfRequired() {
- mockStatic(FileUtils.class);
//when cleverTapApi and settings is null
- when(FileUtils.readFromFile(any(Context.class), anyString())).thenReturn("");
- when(FileUtils.getCachedFullPath(any(Context.class),
- anyString())).thenReturn("");
-
- boolean actualWhenSettingsAndCTApiIsNull = Utils.initCTGeofenceApiIfRequired(application);
- assertFalse("Must be false when cleverTapApi and settings is null", actualWhenSettingsAndCTApiIsNull);
+ try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) {
+ fileUtilsMockedStatic.when(() -> FileUtils.readFromFile(any(Context.class), anyString())).thenReturn("");
+ fileUtilsMockedStatic.when(() -> FileUtils.getCachedFullPath(any(Context.class),
+ anyString())).thenReturn("");
- // when cleverTapApi is null and settings is not null
+ boolean actualWhenSettingsAndCTApiIsNull = Utils.initCTGeofenceApiIfRequired(application);
+ assertFalse("Must be false when cleverTapApi and settings is null", actualWhenSettingsAndCTApiIsNull);
- when(FileUtils.readFromFile(any(Context.class),
- anyString())).thenReturn(CTGeofenceSettingsFake.getSettingsJsonString());
- boolean actualWhenSettingsNonNullAndCTApiIsNull = Utils.initCTGeofenceApiIfRequired(application);
- assertFalse("Must be false when cleverTapApi is null and settings is not null",
- actualWhenSettingsNonNullAndCTApiIsNull);
+ // when cleverTapApi is null and settings is not null
- // when cleverTapApi is not null and settings is not null
+ fileUtilsMockedStatic.when(() -> FileUtils.readFromFile(any(Context.class),
+ anyString())).thenReturn(CTGeofenceSettingsFake.getSettingsJsonString());
+ boolean actualWhenSettingsNonNullAndCTApiIsNull = Utils.initCTGeofenceApiIfRequired(application);
+ assertFalse("Must be false when cleverTapApi is null and settings is not null",
+ actualWhenSettingsNonNullAndCTApiIsNull);
- mockStatic(CleverTapAPI.class);
- CleverTapAPI cleverTapAPI = Mockito.mock(CleverTapAPI.class);
+ // when cleverTapApi is not null and settings is not null
- when(CleverTapAPI.getGlobalInstance(any(Context.class), anyString()))
- .thenReturn(cleverTapAPI);
+ try (MockedStatic clevertapApiMockedStatic = Mockito.mockStatic(CleverTapAPI.class)) {
- boolean actualWhenSettingsNonNullAndCTApiNonNull = Utils.initCTGeofenceApiIfRequired(application);
- assertTrue("Must be true when cleverTapApi is not null and settings is not null",
- actualWhenSettingsNonNullAndCTApiNonNull);
+ clevertapApiMockedStatic.when(() -> CleverTapAPI.getGlobalInstance(any(Context.class), anyString()))
+ .thenReturn(cleverTapAPI);
- // when cleverTapApi is not null
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "cleverTapAPI", cleverTapAPI);
+ boolean actualWhenSettingsNonNullAndCTApiNonNull = Utils.initCTGeofenceApiIfRequired(application);
+ assertTrue("Must be true when cleverTapApi is not null and settings is not null",
+ actualWhenSettingsNonNullAndCTApiNonNull);
- boolean actualWhenCTApiNonNull = Utils.initCTGeofenceApiIfRequired(application);
- assertTrue("Must be true when cleverTapApi is not null",
- actualWhenCTApiNonNull);
+ // when cleverTapApi is not null
+ boolean actualWhenCTApiNonNull = Utils.initCTGeofenceApiIfRequired(application);
+ assertTrue("Must be true when cleverTapApi is not null",
+ actualWhenCTApiNonNull);
+ }
+ }
}
@@ -193,46 +177,48 @@ public void testJsonToGeoFenceList() {
@Test
public void testNotifyLocationUpdates() {
- mockStatic(com.clevertap.android.sdk.Utils.class);
-
- CTLocationUpdatesListener locationUpdatesListener = Mockito.mock(CTLocationUpdatesListener.class);
+ try (MockedStatic coreUtilsMockedStatic = Mockito.mockStatic(
+ com.clevertap.android.sdk.Utils.class)) {
- Mockito.when(ctGeofenceAPI.getCtLocationUpdatesListener()).thenReturn(locationUpdatesListener);
+ CTLocationUpdatesListener locationUpdatesListener = Mockito.mock(CTLocationUpdatesListener.class);
- Utils.notifyLocationUpdates(application, Mockito.mock(Location.class));
+ when(ctGeofenceAPI.getCtLocationUpdatesListener()).thenReturn(locationUpdatesListener);
- ArgumentCaptor runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ Utils.notifyLocationUpdates(application, Mockito.mock(Location.class));
- verifyStatic(com.clevertap.android.sdk.Utils.class);
- com.clevertap.android.sdk.Utils.runOnUiThread(runnableArgumentCaptor.capture());
+ ArgumentCaptor runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
- runnableArgumentCaptor.getValue().run();
- Mockito.verify(locationUpdatesListener).onLocationUpdates(any(Location.class));
+ coreUtilsMockedStatic.verify(
+ () -> com.clevertap.android.sdk.Utils.runOnUiThread(runnableArgumentCaptor.capture()));
+ runnableArgumentCaptor.getValue().run();
+ Mockito.verify(locationUpdatesListener).onLocationUpdates(any(Location.class));
+ }
}
@Test
public void testReadSettingsFromFile() {
-// mockStatic(FileUtils.class);
-//
-// when(FileUtils.getCachedFullPath(any(Context.class),
-// anyString())).thenReturn("");
-//
-// // when settings in file is not blank
-// when(FileUtils.readFromFile(any(Context.class),
-// anyString())).thenReturn(CTGeofenceSettingsFake.getSettingsJsonString());
-//
-// CTGeofenceSettings settingsActualWhenNotEmpty = Utils.readSettingsFromFile(application);
-// CTGeofenceSettings settingsExpectedWhenNotEmpty =
-// CTGeofenceSettingsFake.getSettings(CTGeofenceSettingsFake.getSettingsJsonObject());
-//
-// assertThat(settingsActualWhenNotEmpty, samePropertyValuesAs(settingsExpectedWhenNotEmpty));
-//
-// // when settings in file is blank
-// when(FileUtils.readFromFile(any(Context.class),
-// anyString())).thenReturn("");
-//
-// CTGeofenceSettings settingsActualWhenEmpty = Utils.readSettingsFromFile(application);
-// assertNull(settingsActualWhenEmpty);
+
+ try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) {
+ fileUtilsMockedStatic.when(() -> FileUtils.getCachedFullPath(any(Context.class),
+ anyString())).thenReturn("");
+
+ // when settings in file is not blank
+ fileUtilsMockedStatic.when(() -> FileUtils.readFromFile(any(Context.class),
+ anyString())).thenReturn(CTGeofenceSettingsFake.getSettingsJsonString());
+
+ CTGeofenceSettings settingsActualWhenNotEmpty = Utils.readSettingsFromFile(application);
+ CTGeofenceSettings settingsExpectedWhenNotEmpty =
+ CTGeofenceSettingsFake.getSettings(CTGeofenceSettingsFake.getSettingsJsonObject());
+
+ assertThat(settingsActualWhenNotEmpty, samePropertyValuesAs(settingsExpectedWhenNotEmpty));
+
+ // when settings in file is blank
+ when(FileUtils.readFromFile(any(Context.class),
+ anyString())).thenReturn("");
+
+ CTGeofenceSettings settingsActualWhenEmpty = Utils.readSettingsFromFile(application);
+ assertNull(settingsActualWhenEmpty);
+ }
}
@Test
@@ -248,11 +234,10 @@ public void testSubArray() {
e.printStackTrace();
}
- JSONArray expectedSubArrayFull = geofenceArray;
- JSONArray actualSubArrayFull = Utils.subArray(geofenceArray, 0, expectedSubArrayFull.length());
+ JSONArray actualSubArrayFull = Utils.subArray(geofenceArray, 0, geofenceArray.length());
try {
- JSONAssert.assertEquals(expectedSubArrayFull, actualSubArrayFull, true);
+ JSONAssert.assertEquals(geofenceArray, actualSubArrayFull, true);
} catch (JSONException e) {
e.printStackTrace();
}
@@ -268,29 +253,41 @@ public void testSubArray() {
Assert.assertThrows("IllegalArgumentException must be thrown when fromIndex is greater than lastIndex",
IllegalArgumentException.class,
- new ThrowingRunnable() {
- @Override
- public void run() throws Throwable {
- Utils.subArray(geofenceArray, geofenceArray.length(), 0);
- }
- });
+ () -> Utils.subArray(geofenceArray, geofenceArray.length(), 0));
}
@Test
- public void testWriteSettingsToFile() {
- mockStatic(FileUtils.class);
-
- WhiteboxImpl.setInternalState(ctGeofenceAPI, "accountId", "4RW-Z6Z-485Z");
- when(FileUtils.getCachedDirName(application)).thenReturn("");
-
- Utils.writeSettingsToFile(application,
- CTGeofenceSettingsFake.getSettings(CTGeofenceSettingsFake.getSettingsJsonObject()));
-
- verifyStatic(FileUtils.class);
- FileUtils.writeJsonToFile(application, FileUtils.getCachedDirName(application),
- CTGeofenceConstants.SETTINGS_FILE_NAME, CTGeofenceSettingsFake.getSettingsJsonObject());
-
+ public void testWriteSettingsToFileFailure() {
+
+ try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) {
+ when(FileUtils.getCachedDirName(application)).thenReturn("");
+ when(FileUtils.writeJsonToFile(any(), any(), any(), any())).thenReturn(false);
+
+ Utils.writeSettingsToFile(application,
+ CTGeofenceSettingsFake.getSettings(CTGeofenceSettingsFake.getSettingsJsonObject()));
+ fileUtilsMockedStatic.verify(
+ () -> FileUtils.writeJsonToFile(application, FileUtils.getCachedDirName(application),
+ CTGeofenceConstants.SETTINGS_FILE_NAME, CTGeofenceSettingsFake.getSettingsJsonObject()));
+ Mockito.verify(logger).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "Failed to write new settings to file");
+ }
}
+ @Test
+ public void testWriteSettingsToFileSuccess() {
+
+ try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) {
+ when(FileUtils.getCachedDirName(application)).thenReturn("");
+ when(FileUtils.writeJsonToFile(any(), any(), any(), any())).thenReturn(true);
+
+ Utils.writeSettingsToFile(application,
+ CTGeofenceSettingsFake.getSettings(CTGeofenceSettingsFake.getSettingsJsonObject()));
+ fileUtilsMockedStatic.verify(
+ () -> FileUtils.writeJsonToFile(application, FileUtils.getCachedDirName(application),
+ CTGeofenceConstants.SETTINGS_FILE_NAME, CTGeofenceSettingsFake.getSettingsJsonObject()));
+ Mockito.verify(logger).debug(CTGeofenceAPI.GEOFENCE_LOG_TAG,
+ "New settings successfully written to file");
+ }
+ }
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a4ef03bbe..257652497 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -41,13 +41,8 @@ kotlin_test = "1.7.20" #check with kotlin_gradle_plugin
junit_jupiter_api = "5.7.2"
junit_jupiter_engine = "5.7.2"
junit_platform_runner = "1.7.2"
-mockito_core = "3.5.11"
+mockito_core = "5.9.0"
opentest4j = "1.2.0"
-powermock_api_mockito2 = "2.0.9"
-powermock_classloading_xstream = "2.0.9"
-powermock_core = "2.0.9"
-powermock_module_junit4 = "2.0.9"
-powermock_module_junit4_rule = "2.0.9"
robolectric = "4.9"
jsonassert = "1.5.0"
xmlpull = "1.1.3.1"
@@ -134,13 +129,7 @@ test_junit_jupiter_api = { module = "org.junit.jupiter:junit-jupiter-api", versi
test_junit_jupiter_engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit_jupiter_engine" }
test_junit_platform_runner = { module = "org.junit.platform:junit-platform-runner", version.ref = "junit_platform_runner" }
test_mockito_core = { module = "org.mockito:mockito-core", version.ref = "mockito_core" }
-test_mockito_inline = { module = "org.mockito:mockito-inline", version.ref = "mockito_core" }
test_opentest4j = { module = "org.opentest4j:opentest4j", version.ref = "opentest4j" }
-test_powermock_api_mockito2 = { module = "org.powermock:powermock-api-mockito2", version.ref = "powermock_api_mockito2" }
-test_powermock_classloading_xstream = { module = "org.powermock:powermock-classloading-xstream", version.ref = "powermock_classloading_xstream" }
-test_powermock_core = { module = "org.powermock:powermock-core", version.ref = "powermock_core" }
-test_powermock_module_junit4 = { module = "org.powermock:powermock-module-junit4", version.ref = "powermock_module_junit4" }
-test_powermock_module_junit4_rule = { module = "org.powermock:powermock-module-junit4-rule", version.ref = "powermock_module_junit4_rule" }
test_robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
test_jsonassert = { module = "org.skyscreamer:jsonassert", version.ref = "jsonassert" }
test_xmlpull = { module = "xmlpull:xmlpull", version.ref = "xmlpull" }
@@ -192,8 +181,6 @@ catch_exception = { module = "eu.codearte.catch-exception:catch-exception", vers
[bundles]
exoplayer = ["exoplayer_exoplayer", "exoplayer_hls", "exoplayer_ui"]
-powermock = ["test_powermock_api_mockito2", "test_powermock_classloading_xstream", "test_powermock_core", "test_powermock_module_junit4", "test_powermock_module_junit4_rule"]
-mockito = ["test_mockito_core", "test_mockito_inline"]
[plugins]
sonarqube = { id="org.sonarqube", version.ref = "sonarqube_plugin" }
\ No newline at end of file
diff --git a/test_shared/build.gradle b/test_shared/build.gradle
index 0b5f9bdee..241e1a06b 100644
--- a/test_shared/build.gradle
+++ b/test_shared/build.gradle
@@ -34,7 +34,7 @@ dependencies {
api (libs.kotlin.stdlib.jdk7)
api (libs.test.jsonassert)
api (libs.gson)
- api (libs.bundles.mockito)
+ api (libs.test.mockito.core)
api (libs.test.robolectric)
api (libs.test.opentest4j)
From 3f6d6e823e12a95eb58665fa4682e467eb57745a Mon Sep 17 00:00:00 2001
From: anush
Date: Fri, 9 Feb 2024 12:07:46 +0530
Subject: [PATCH 09/29] task(SDK-3620) - Removes changes not specific to this
task
---
clevertap-core/build.gradle | 1 -
gradle/libs.versions.toml | 6 +++---
sample/build.gradle | 6 +++---
3 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/clevertap-core/build.gradle b/clevertap-core/build.gradle
index 2cbf5139d..74399bf63 100644
--- a/clevertap-core/build.gradle
+++ b/clevertap-core/build.gradle
@@ -29,7 +29,6 @@ android {
jvmTarget = JavaVersion.VERSION_1_8
}
namespace 'com.clevertap.android.sdk'
- testNamespace 'com.clevertap.demo'
}
dependencies {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index db343ca1d..51bea6994 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,12 +1,12 @@
[versions]
# Project
-android_compileSdk = "34"
+android_compileSdk = "33"
android_gradle_plugin = "8.2.1"
android_minSdk = "19"
-android_targetSdk = "34"
+android_targetSdk = "33"
kotlin_plugin = "1.9.0"
sonarqube_plugin = "3.3"
-android_buildTools = "34.0.0"
+android_buildTools = "33.0.0"
detekt_gradle_plugin = "1.20.0-RC1"
firebase_gradle_crashlytics = "2.8.1"
diff --git a/sample/build.gradle b/sample/build.gradle
index 5e740d7bb..7cabfee97 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -13,11 +13,11 @@ allprojects {
}
android {
- compileSdk 34
+ compileSdk 33
defaultConfig {
applicationId "com.clevertap.demo"
minSdkVersion 21
- targetSdkVersion 34
+ targetSdkVersion 33
versionCode 200005
versionName "1.6.2-full"
multiDexEnabled true
@@ -142,7 +142,7 @@ dependencies {
remoteImplementation("com.clevertap.android:clevertap-geofence-sdk:1.2.0")
remoteImplementation("com.clevertap.android:clevertap-xiaomi-sdk:1.5.2")
remoteImplementation("com.clevertap.android:push-templates:1.1.0")
- remoteImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.3")
+ remoteImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.2")
stagingImplementation("com.clevertap.android:clevertap-android-sdk:5.2.1")
stagingImplementation("com.clevertap.android:clevertap-geofence-sdk:1.2.0")
From bb1ffa4653eb21b8f8e3bd8db3ee5ceb191f85ef Mon Sep 17 00:00:00 2001
From: anush
Date: Fri, 9 Feb 2024 12:10:14 +0530
Subject: [PATCH 10/29] task(SDK-3620) - Adds test namespace for core-sdk
---
clevertap-core/build.gradle | 1 +
1 file changed, 1 insertion(+)
diff --git a/clevertap-core/build.gradle b/clevertap-core/build.gradle
index 74399bf63..2cbf5139d 100644
--- a/clevertap-core/build.gradle
+++ b/clevertap-core/build.gradle
@@ -29,6 +29,7 @@ android {
jvmTarget = JavaVersion.VERSION_1_8
}
namespace 'com.clevertap.android.sdk'
+ testNamespace 'com.clevertap.demo'
}
dependencies {
From 2543bcb678d4070f86125517ccd99c7906173a88 Mon Sep 17 00:00:00 2001
From: anush
Date: Fri, 9 Feb 2024 14:56:19 +0530
Subject: [PATCH 11/29] task(SDK-3620) - Possible fix for failing git actions
---
clevertap-hms/build.gradle | 8 ++++++++
test_shared/build.gradle | 6 +++---
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/clevertap-hms/build.gradle b/clevertap-hms/build.gradle
index 0f4cb92c2..a0207f005 100644
--- a/clevertap-hms/build.gradle
+++ b/clevertap-hms/build.gradle
@@ -20,6 +20,14 @@ apply from: "../gradle-scripts/commons.gradle"
android {
namespace 'com.clevertap.android.hms'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8
+ }
}
dependencies {
diff --git a/test_shared/build.gradle b/test_shared/build.gradle
index 692225283..ae813cf9b 100644
--- a/test_shared/build.gradle
+++ b/test_shared/build.gradle
@@ -21,11 +21,11 @@ android {
}
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_17
- targetCompatibility JavaVersion.VERSION_17
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_17
+ jvmTarget = JavaVersion.VERSION_1_8
}
namespace 'com.clevertap.android.shared.test'
From 06e565d1cf1958affbc8e7f261d352d84e05c7d4 Mon Sep 17 00:00:00 2001
From: anush
Date: Fri, 9 Feb 2024 15:08:36 +0530
Subject: [PATCH 12/29] task(SDK-3620) - Possible fix for failing git actions
---
clevertap-geofence/build.gradle | 3 +++
clevertap-xps/build.gradle | 3 +++
2 files changed, 6 insertions(+)
diff --git a/clevertap-geofence/build.gradle b/clevertap-geofence/build.gradle
index 008f3bada..ddba00165 100644
--- a/clevertap-geofence/build.gradle
+++ b/clevertap-geofence/build.gradle
@@ -14,6 +14,9 @@ ext {
apply from: "../gradle-scripts/commons.gradle"
android {
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8
+ }
namespace 'com.clevertap.android.geofence'
}
diff --git a/clevertap-xps/build.gradle b/clevertap-xps/build.gradle
index a769ab290..23dded1f4 100644
--- a/clevertap-xps/build.gradle
+++ b/clevertap-xps/build.gradle
@@ -19,6 +19,9 @@ android{
dirs 'libs'
}
}
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8
+ }
namespace 'com.clevertap.android.xps'
}
From 87864c5667fec20b88d74973703b735942f2949b Mon Sep 17 00:00:00 2001
From: anush
Date: Fri, 9 Feb 2024 15:24:44 +0530
Subject: [PATCH 13/29] task(SDK-3620) - Adds compileOptions for all modules
---
clevertap-geofence/build.gradle | 4 ++++
clevertap-xps/build.gradle | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/clevertap-geofence/build.gradle b/clevertap-geofence/build.gradle
index ddba00165..fe5bf5d9b 100644
--- a/clevertap-geofence/build.gradle
+++ b/clevertap-geofence/build.gradle
@@ -14,6 +14,10 @@ ext {
apply from: "../gradle-scripts/commons.gradle"
android {
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
diff --git a/clevertap-xps/build.gradle b/clevertap-xps/build.gradle
index 23dded1f4..050dd604d 100644
--- a/clevertap-xps/build.gradle
+++ b/clevertap-xps/build.gradle
@@ -19,6 +19,10 @@ android{
dirs 'libs'
}
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
From 467893294a69d1adfb1d97c0b79c520b91ff24ca Mon Sep 17 00:00:00 2001
From: Anush-Shand <127097095+Anush-Shand@users.noreply.github.com>
Date: Tue, 13 Feb 2024 11:41:18 +0530
Subject: [PATCH 14/29] task(SDK-3536) - Changes inapp fragment transaction to
commitNow() (#540)
---
.../com/clevertap/android/sdk/InAppNotificationActivity.java | 2 +-
.../java/com/clevertap/android/sdk/inapp/InAppController.java | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java
index 223e5fa4b..62a5f119f 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/InAppNotificationActivity.java
@@ -135,7 +135,7 @@ public void onCreate(Bundle savedInstanceState) {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.add(android.R.id.content, contentFragment, getFragmentTag())
- .commit();
+ .commitNow();
}
} else if (isAlertVisible) {
createContentFragment();
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/InAppController.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/InAppController.java
index cb2eb6d05..2086632f0 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/InAppController.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/InAppController.java
@@ -214,7 +214,7 @@ public void checkExistingInAppNotifications(Activity activity) {
fragmentTransaction.add(android.R.id.content, inAppFragment, currentlyDisplayingInApp.getType());
Logger.v(config.getAccountId(),
"calling InAppFragment " + currentlyDisplayingInApp.getCampaignId());
- fragmentTransaction.commit();
+ fragmentTransaction.commitNow();
}
}
}
@@ -782,7 +782,7 @@ private static void showInApp(Context context, final CTInAppNotification inAppNo
fragmentTransaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
fragmentTransaction.add(android.R.id.content, inAppFragment, inAppNotification.getType());
Logger.v(config.getAccountId(), "calling InAppFragment " + inAppNotification.getCampaignId());
- fragmentTransaction.commit();
+ fragmentTransaction.commitNow();
} catch (ClassCastException e) {
Logger.v(config.getAccountId(),
From 37f56f2cef93c68ef8b455f4fce2db0f96649e42 Mon Sep 17 00:00:00 2001
From: Anush-Shand <127097095+Anush-Shand@users.noreply.github.com>
Date: Tue, 13 Feb 2024 12:29:49 +0530
Subject: [PATCH 15/29] task(SDK-3238) - Fixes Input PT by changing flag to
MUTABLE (#544)
---
.../content/PendingIntentFactory.kt | 31 ++++++++++---------
1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/content/PendingIntentFactory.kt b/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/content/PendingIntentFactory.kt
index 27421d488..d4bf45061 100644
--- a/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/content/PendingIntentFactory.kt
+++ b/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/content/PendingIntentFactory.kt
@@ -3,7 +3,6 @@ package com.clevertap.android.pushtemplates.content
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Bundle
@@ -69,8 +68,14 @@ internal object PendingIntentFactory {
launchIntent.flags =
Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
var flagsLaunchPendingIntent = PendingIntent.FLAG_UPDATE_CURRENT
- if (Build.VERSION.SDK_INT >= VERSION_CODES.S) {
- flagsLaunchPendingIntent = flagsLaunchPendingIntent or PendingIntent.FLAG_IMMUTABLE
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ flagsLaunchPendingIntent = flagsLaunchPendingIntent or
+ if (launchIntent.hasExtra(PTConstants.PT_INPUT_FEEDBACK)) {
+ // PendingIntents attached to actions with remote inputs must be mutable
+ PendingIntent.FLAG_MUTABLE
+ } else {
+ PendingIntent.FLAG_IMMUTABLE
+ }
}
return PendingIntent.getBroadcast(
context, requestCode,
@@ -85,7 +90,7 @@ internal object PendingIntentFactory {
intent.putExtra(PTConstants.PT_DISMISS_INTENT, true)
var flagsLaunchPendingIntent = PendingIntent.FLAG_CANCEL_CURRENT
- if (Build.VERSION.SDK_INT >= VERSION_CODES.S) {
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
flagsLaunchPendingIntent = flagsLaunchPendingIntent or PendingIntent.FLAG_IMMUTABLE
}
return PendingIntent.getBroadcast(
@@ -304,18 +309,16 @@ internal object PendingIntentFactory {
launchIntent!!.putExtra(PTConstants.PT_INPUT_AUTO_OPEN, renderer?.pt_input_auto_open)
launchIntent!!.putExtra("config", renderer?.config)
- return if (renderer?.deepLinkList != null) {
- setPendingIntent(
- context,
- notificationId,
- extras,
- launchIntent,
- requestCode
- )
- } else {
+ if (renderer.deepLinkList == null) {
extras.putString(Constants.DEEP_LINK_KEY, null)
- setPendingIntent(context, notificationId, extras, launchIntent, requestCode)
}
+ return setPendingIntent(
+ context,
+ notificationId,
+ extras,
+ launchIntent,
+ requestCode
+ )
}
else -> throw IllegalArgumentException("invalid pendingIntentType")
}
From 092cb5385394e883790bae51fc4e44b6fb37077e Mon Sep 17 00:00:00 2001
From: anush
Date: Tue, 13 Feb 2024 14:47:51 +0530
Subject: [PATCH 16/29] task(SDK-3620) - Adds agp 8 related consumer proguard
rules for hms and baidu
---
clevertap-core/consumer-rules.pro | 1 +
clevertap-hms/consumer-rules.pro | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/clevertap-core/consumer-rules.pro b/clevertap-core/consumer-rules.pro
index f4c95017b..5dfc6ebb2 100644
--- a/clevertap-core/consumer-rules.pro
+++ b/clevertap-core/consumer-rules.pro
@@ -13,5 +13,6 @@
public static final ** CREATOR;
}
-dontwarn com.clevertap.android.sdk.**
+-dontwarn com.baidu.**
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
\ No newline at end of file
diff --git a/clevertap-hms/consumer-rules.pro b/clevertap-hms/consumer-rules.pro
index 7648539a9..5018b0a92 100644
--- a/clevertap-hms/consumer-rules.pro
+++ b/clevertap-hms/consumer-rules.pro
@@ -1,2 +1,3 @@
-keep class com.huawei.**{*;}
--keep class com.hianalytics.android.**{*;}
\ No newline at end of file
+-keep class com.hianalytics.android.**{*;}
+-dontwarn com.huawei.**
\ No newline at end of file
From 9ac487c78833895bc29067445dcd87fd0bc7b770 Mon Sep 17 00:00:00 2001
From: anush
Date: Tue, 13 Feb 2024 14:48:23 +0530
Subject: [PATCH 17/29] task(SDK-3620) - Updates sample app
---
sample/build.gradle | 1 -
sample/proguard-rules.pro | 2 --
2 files changed, 3 deletions(-)
diff --git a/sample/build.gradle b/sample/build.gradle
index 7cabfee97..15925c5f9 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -99,7 +99,6 @@ dependencies {
localImplementation(project(":clevertap-pushtemplates"))
localImplementation(project(":clevertap-hms")) // For Huawei Push use
- implementation("com.huawei.hms:push:6.5.0.300")
implementation("com.google.android.gms:play-services-location:21.0.0") // Needed for geofence
implementation("androidx.work:work-runtime:2.7.1") // Needed for geofence
implementation("androidx.concurrent:concurrent-futures:1.1.0") // Needed for geofence
diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro
index 8d991ab21..dd9cc0560 100644
--- a/sample/proguard-rules.pro
+++ b/sample/proguard-rules.pro
@@ -19,12 +19,10 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
--dontwarn com.clevertap.android.sdk.**
-keep class androidx.core.app.CoreComponentFactory { *; }
-keep class com.xiaomi.mipush.**{*;}
-keep class com.huawei.**{*;}
--ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
From 8eddc520750c5be09852efdfeb201935bd08e694 Mon Sep 17 00:00:00 2001
From: CTLalit <144685420+CTLalit@users.noreply.github.com>
Date: Fri, 16 Feb 2024 11:52:40 +0530
Subject: [PATCH 18/29] Lp/knock/anr jio (#548)
* Bug: theoretical ANR due to synchronised method
- my jio reported an anr trace which shows in register method of SDK
- while there is nothing major happening to cause this anr, we are removing synchronised keyword because it seems not absolutely necessary.
- Jira : https://wizrocket.atlassian.net/browse/SDK-3630
* Chore: Removal of log
- removed log
---
.../sdk/ActivityLifecycleCallback.java | 96 ++++++++++---------
1 file changed, 49 insertions(+), 47 deletions(-)
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/ActivityLifecycleCallback.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/ActivityLifecycleCallback.java
index 7eeb199b7..2aa283d27 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/ActivityLifecycleCallback.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/ActivityLifecycleCallback.java
@@ -2,10 +2,9 @@
import android.annotation.TargetApi;
import android.app.Activity;
-import android.content.Context;
+import android.app.Application;
import android.os.Build;
import android.os.Bundle;
-import java.util.HashSet;
/**
* Class for handling activity lifecycle events
@@ -14,6 +13,49 @@
public final class ActivityLifecycleCallback {
public static boolean registered = false;
+ private static String cleverTapId = null;
+ private static final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) {
+ if (cleverTapId != null) {
+ CleverTapAPI.onActivityCreated(activity, cleverTapId);
+ } else {
+ CleverTapAPI.onActivityCreated(activity);
+ }
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ CleverTapAPI.onActivityPaused();
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ if (cleverTapId != null) {
+ CleverTapAPI.onActivityResumed(activity, cleverTapId);
+ } else {
+ CleverTapAPI.onActivityResumed(activity);
+ }
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ }
+ };
+
/**
* Enables lifecycle callbacks for Android devices
*
@@ -21,7 +63,7 @@ public final class ActivityLifecycleCallback {
* @param cleverTapID Custom CleverTap ID
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- public static synchronized void register(android.app.Application application, final String cleverTapID) {
+ public static void register(android.app.Application application, final String cleverTapID) {
if (application == null) {
Logger.i("Application instance is null/system API is too old");
return;
@@ -32,51 +74,11 @@ public static synchronized void register(android.app.Application application, fi
return;
}
+ cleverTapId = cleverTapID;
registered = true;
- application.registerActivityLifecycleCallbacks(
- new android.app.Application.ActivityLifecycleCallbacks() {
-
- @Override
- public void onActivityCreated(Activity activity, Bundle bundle) {
- if (cleverTapID != null) {
- CleverTapAPI.onActivityCreated(activity, cleverTapID);
- } else {
- CleverTapAPI.onActivityCreated(activity);
- }
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- CleverTapAPI.onActivityPaused();
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
- if (cleverTapID != null) {
- CleverTapAPI.onActivityResumed(activity, cleverTapID);
- } else {
- CleverTapAPI.onActivityResumed(activity);
- }
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
- }
-
- @Override
- public void onActivityStarted(Activity activity) {
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- }
- }
- );
+ application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
+ application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
Logger.i("Activity Lifecycle Callback successfully registered");
}
@@ -86,7 +88,7 @@ public void onActivityStopped(Activity activity) {
* @param application App's Application object
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- public static synchronized void register(android.app.Application application) {
+ public static void register(android.app.Application application) {
register(application, null);
}
}
From 8cb6333b433e9fa744c1149a505ca80736beb7e5 Mon Sep 17 00:00:00 2001
From: Anush-Shand <127097095+Anush-Shand@users.noreply.github.com>
Date: Fri, 16 Feb 2024 11:54:47 +0530
Subject: [PATCH 19/29] Task/sdk-3622/upgrade to android 14 (#549)
* task(SDK-3629) - Handles deprecated overridePendingTransitionMethod()
* task(SDK-3238) - Adds activity options to PendingIntent.getActivity()
* task(SDK-3238) - Updates compileSDKVersion and targetSDKVersion
* task(SDK-3238) - Fixes unit tests by updating workflow files to target java version 17
* task(SDK-3238) - Fixes unit tests
* task(SDK-3238) - Reverts changes
* task(SDK-3238) - Fixes failing git actions
* task(SDK-3238) - Reverts changes
* task(SDK-3238) - Fixes unit tests
* task(SDK-3622) - Removes targetSDKVersion and deprecated features
* Task/sdk 3236/migrate to work manager (#542)
* task(SDK-3236) - Initial implementation for migration to WorkManager
* task(SDK-3236) - Adds main process check to before init push amo
* task(SDK-3236) - Cleans the code and adds an instrumentation test
* task(SDK-3236) - Removes redundant files
* task(SDK-3236) - Cleans redundant code
* task(SDK-3236) - Updates PushAmpWorker test
* task(SDK-3236) - Updates logic for pingFrequencyUpdate
* task(SDK-3236) - Updates pingFrequency
* task(SDK-3236) - Adds comments and exception handling
* task(SDK-3236) - Updates logic for stopping worker
* task(SDK-3236) - Cancels jobscheduler jobs on app update
* task(SDK-3236) - Moves constants to specific class, removes postAsyncSafely for ping event, improves logging, adds default flex interval
* task(SDK-3236) - Optimizes SDF object creation
* task(SDK-3236) - Optimizes SDF object creation
---
.../src/androidTest/AndroidManifest.xml | 3 +
.../CTPushAmpWorkerInstrumentationTest.kt | 63 ++++
clevertap-core/src/main/AndroidManifest.xml | 14 -
.../clevertap/android/sdk/CleverTapAPI.java | 37 +-
.../com/clevertap/android/sdk/Constants.java | 3 -
.../sdk/InAppNotificationActivity.java | 17 +-
.../INotificationRenderer.java | 8 +-
.../LaunchPendingIntentFactory.java | 10 +-
.../sdk/pushnotification/PushProviders.java | 335 +++++++-----------
.../amp/CTBackgroundIntentService.java | 26 --
.../amp/CTBackgroundJobService.java | 34 --
.../pushnotification/amp/CTPushAmpWorker.kt | 16 +
.../sdk/validation/ManifestValidator.java | 8 -
.../android/sdk/network/api/CtApiTest.kt | 2 +
.../amp/CTBackgroundIntentServiceTest.kt | 37 --
.../amp/CTBackgroundJobServiceTest.kt | 29 --
.../android/pushtemplates/TemplateRenderer.kt | 11 +-
gradle-scripts/commons.gradle | 4 +-
gradle/libs.versions.toml | 4 +-
sample/build.gradle | 4 +-
test_shared/build.gradle | 4 +-
21 files changed, 259 insertions(+), 410 deletions(-)
create mode 100644 clevertap-core/src/androidTest/kotlin/CTPushAmpWorkerInstrumentationTest.kt
delete mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentService.java
delete mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobService.java
create mode 100644 clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTPushAmpWorker.kt
delete mode 100644 clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentServiceTest.kt
delete mode 100644 clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobServiceTest.kt
diff --git a/clevertap-core/src/androidTest/AndroidManifest.xml b/clevertap-core/src/androidTest/AndroidManifest.xml
index 108978520..8c6e8bda2 100644
--- a/clevertap-core/src/androidTest/AndroidManifest.xml
+++ b/clevertap-core/src/androidTest/AndroidManifest.xml
@@ -15,6 +15,9 @@
+
diff --git a/clevertap-core/src/androidTest/kotlin/CTPushAmpWorkerInstrumentationTest.kt b/clevertap-core/src/androidTest/kotlin/CTPushAmpWorkerInstrumentationTest.kt
new file mode 100644
index 000000000..9d65c31e9
--- /dev/null
+++ b/clevertap-core/src/androidTest/kotlin/CTPushAmpWorkerInstrumentationTest.kt
@@ -0,0 +1,63 @@
+import android.content.Context
+import android.util.Log
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.work.Configuration
+import androidx.work.Constraints.Builder
+import androidx.work.NetworkType.CONNECTED
+import androidx.work.PeriodicWorkRequest
+import androidx.work.WorkInfo
+import androidx.work.WorkManager
+import androidx.work.testing.SynchronousExecutor
+import androidx.work.testing.WorkManagerTestInitHelper
+import com.clevertap.android.sdk.CleverTapAPI
+import com.clevertap.android.sdk.CleverTapAPI.LogLevel.VERBOSE
+import com.clevertap.android.sdk.pushnotification.amp.CTPushAmpWorker
+import org.hamcrest.CoreMatchers.*
+import org.hamcrest.MatcherAssert.*
+import org.junit.*
+import org.junit.runner.*
+import java.util.concurrent.TimeUnit.MINUTES
+
+@RunWith(AndroidJUnit4::class)
+class CTPushAmpWorkerInstrumentationTest {
+ @Before
+ fun setup() {
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
+ val config = Configuration.Builder()
+ .setMinimumLoggingLevel(Log.VERBOSE)
+ .setExecutor(SynchronousExecutor())
+ .build()
+
+ // Initialize WorkManager for instrumentation tests.
+ WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
+ }
+
+ @Test
+ fun testWork(){
+ CleverTapAPI.setDebugLevel(VERBOSE)
+ val myContext = ApplicationProvider.getApplicationContext()
+
+ val constraints = Builder()
+ .setRequiredNetworkType(CONNECTED)
+ .setRequiresCharging(false)
+ .setRequiresBatteryNotLow(true)
+ .build()
+
+ val request =
+ PeriodicWorkRequest.Builder(CTPushAmpWorker::class.java, 15, MINUTES, 5, MINUTES)
+ .setConstraints(constraints).build()
+
+ val workManager = WorkManager.getInstance(myContext)
+ val testDriver = WorkManagerTestInitHelper.getTestDriver(myContext)!!
+ // Enqueue
+ workManager.enqueue(request).result.get()
+ testDriver.setAllConstraintsMet(request.id)
+ testDriver.setPeriodDelayMet(request.id)
+ val workInfo = workManager.getWorkInfoById(request.id).get()
+ println("workInfo = $workInfo")
+ // Assert
+ assertThat(workInfo.state, `is`(WorkInfo.State.ENQUEUED))
+ }
+}
\ No newline at end of file
diff --git a/clevertap-core/src/main/AndroidManifest.xml b/clevertap-core/src/main/AndroidManifest.xml
index c7d6b5295..d38fcc1fa 100644
--- a/clevertap-core/src/main/AndroidManifest.xml
+++ b/clevertap-core/src/main/AndroidManifest.xml
@@ -22,20 +22,6 @@
android:enabled="true"
android:exported="false" />
-
-
-
-
-
-
-
-
= VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, android.R.anim.fade_in, android.R.anim.fade_out);
+ } else {
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ }
if (invokedInAppDismissCallback) {
return;
@@ -170,11 +177,15 @@ public void finish() {
notifyInAppDismissed();
}
+ @SuppressLint("WrongConstant")
@Override
protected void onDestroy() {
super.onDestroy();
- overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
-
+ if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, android.R.anim.fade_in, android.R.anim.fade_out);
+ } else {
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ }
if (invokedInAppDismissCallback) {
return;
}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/INotificationRenderer.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/INotificationRenderer.java
index 141999e15..8d2a35d9c 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/INotificationRenderer.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/INotificationRenderer.java
@@ -1,5 +1,6 @@
package com.clevertap.android.sdk.pushnotification;
+import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -148,8 +149,13 @@ default NotificationCompat.Builder setActionButtons(
actionIntent = PendingIntent.getService(context, requestCode,
actionLaunchIntent, flagsActionLaunchPendingIntent);
} else {
+ Bundle optionsBundle = null;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ optionsBundle = ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle();
+ }
actionIntent = PendingIntent.getActivity(context, requestCode,
- actionLaunchIntent, flagsActionLaunchPendingIntent);
+ actionLaunchIntent, flagsActionLaunchPendingIntent, optionsBundle);
}
nb.addAction(icon, label, actionIntent);
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/LaunchPendingIntentFactory.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/LaunchPendingIntentFactory.java
index 936474481..24d8554ef 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/LaunchPendingIntentFactory.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/LaunchPendingIntentFactory.java
@@ -1,5 +1,6 @@
package com.clevertap.android.sdk.pushnotification;
+import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -7,7 +8,6 @@
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
-import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import com.clevertap.android.sdk.Constants;
@@ -64,8 +64,14 @@ public static PendingIntent getActivityIntent(@NonNull Bundle extras, @NonNull C
flagsLaunchPendingIntent |= PendingIntent.FLAG_IMMUTABLE;
}
+ Bundle optionsBundle = null;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ optionsBundle = ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle();
+ }
+
return PendingIntent.getActivity(context, new Random().nextInt(), launchIntent,
- flagsLaunchPendingIntent);
+ flagsLaunchPendingIntent, optionsBundle);
}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java
index 83f2653e3..924ea4f34 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java
@@ -6,20 +6,13 @@
import static com.clevertap.android.sdk.pushnotification.PushNotificationUtil.getPushTypes;
import android.annotation.SuppressLint;
-import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
import android.app.job.JobScheduler;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
-import android.os.SystemClock;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -27,6 +20,11 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.core.app.NotificationCompat;
+import androidx.work.Constraints;
+import androidx.work.ExistingPeriodicWorkPolicy;
+import androidx.work.NetworkType;
+import androidx.work.PeriodicWorkRequest;
+import androidx.work.WorkManager;
import com.clevertap.android.sdk.AnalyticsManager;
import com.clevertap.android.sdk.CTXtensions;
import com.clevertap.android.sdk.CleverTapAPI.DevicePushTokenRefreshListener;
@@ -42,8 +40,7 @@
import com.clevertap.android.sdk.db.DBAdapter;
import com.clevertap.android.sdk.interfaces.AudibleNotification;
import com.clevertap.android.sdk.pushnotification.PushConstants.PushType;
-import com.clevertap.android.sdk.pushnotification.amp.CTBackgroundIntentService;
-import com.clevertap.android.sdk.pushnotification.amp.CTBackgroundJobService;
+import com.clevertap.android.sdk.pushnotification.amp.CTPushAmpWorker;
import com.clevertap.android.sdk.pushnotification.work.CTWorkManager;
import com.clevertap.android.sdk.task.CTExecutorFactory;
import com.clevertap.android.sdk.task.Task;
@@ -59,6 +56,7 @@
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
import org.json.JSONException;
import org.json.JSONObject;
@@ -69,6 +67,13 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public class PushProviders implements CTPushProviderListener {
+ private static final int DEFAULT_FLEX_INTERVAL = 5;
+ private static final int PING_FREQUENCY_VALUE = 240;
+ private static final String PF_JOB_ID = "pfjobid";
+ private static final String PF_WORK_ID = "pfworkid";
+ private static final String PING_FREQUENCY = "pf";
+ private static final String inputFormat = "HH:mm";
+
private final ArrayList allEnabledPushTypes = new ArrayList<>();
private final ArrayList allDisabledPushTypes = new ArrayList<>();
@@ -417,16 +422,10 @@ public void updatePingFrequencyIfNeeded(final Context context, int frequency) {
setPingFrequency(context, frequency);
if (config.isBackgroundSync() && !config.isAnalyticsOnly()) {
Task task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
- task.execute("createOrResetJobScheduler", new Callable() {
+ task.execute("createOrResetWorker", new Callable() {
@Override
public Void call() {
- if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- config.getLogger().verbose("Creating job");
- createOrResetJobScheduler(context);
- } else {
- config.getLogger().verbose("Resetting alarm");
- resetAlarmScheduler(context);
- }
+ createOrResetWorker(true);
return null;
}
});
@@ -443,145 +442,122 @@ private boolean alreadyHaveToken(String newToken, PushType pushType) {
return alreadyAvailable;
}
- public void runInstanceJobWork(final Context context, final JobParameters parameters) {
- Task task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
- task.execute("runningJobService", new Callable() {
- @Override
- public Void call() {
- if (!isNotificationSupported()) {
- Logger.v(config.getAccountId(), "Token is not present, not running the Job");
- return null;
- }
-
- Calendar now = Calendar.getInstance();
-
- int hour = now.get(Calendar.HOUR_OF_DAY); // Get hour in 24 hour format
- int minute = now.get(Calendar.MINUTE);
+ public void runPushAmpWork(final Context context) {
+ Logger.v(config.getAccountId(), "Pushamp - Running work request");
+ if (!isNotificationSupported()) {
+ Logger.v(config.getAccountId(), "Pushamp - Token is not present, not running the work request");
+ return;
+ }
- Date currentTime = parseTimeToDate(hour + ":" + minute);
- Date startTime = parseTimeToDate(Constants.DND_START);
- Date endTime = parseTimeToDate(Constants.DND_STOP);
+ Calendar now = Calendar.getInstance();
- if (isTimeBetweenDNDTime(startTime, endTime, currentTime)) {
- Logger.v(config.getAccountId(), "Job Service won't run in default DND hours");
- return null;
- }
+ int hour = now.get(Calendar.HOUR_OF_DAY); // Get hour in 24 hour format
+ int minute = now.get(Calendar.MINUTE);
- long lastTS = baseDatabaseManager.loadDBAdapter(context).getLastUninstallTimestamp();
+ final SimpleDateFormat inputParser = new SimpleDateFormat(inputFormat, Locale.US);
- if (lastTS == 0 || lastTS > System.currentTimeMillis() - 24 * 60 * 60 * 1000) {
- try {
- JSONObject eventObject = new JSONObject();
- eventObject.put("bk", 1);
- analyticsManager.sendPingEvent(eventObject);
+ Date currentTime = parseTimeToDate(hour + ":" + minute, inputParser);
+ Date startTime = parseTimeToDate(Constants.DND_START, inputParser);
+ Date endTime = parseTimeToDate(Constants.DND_STOP, inputParser);
- int flagsAlarmPendingIntent = PendingIntent.FLAG_UPDATE_CURRENT;
- if (VERSION.SDK_INT >= VERSION_CODES.M) {
- flagsAlarmPendingIntent |= PendingIntent.FLAG_IMMUTABLE;
- }
+ if (isTimeBetweenDNDTime(startTime, endTime, currentTime)) {
+ Logger.v(config.getAccountId(), "Pushamp won't run in default DND hours");
+ return;
+ }
- if (parameters == null) {
- int pingFrequency = getPingFrequency(context);
- AlarmManager alarmManager = (AlarmManager) context
- .getSystemService(Context.ALARM_SERVICE);
- Intent cancelIntent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- cancelIntent.setPackage(context.getPackageName());
- PendingIntent alarmPendingIntent = PendingIntent
- .getService(context, config.getAccountId().hashCode(), cancelIntent,
- flagsAlarmPendingIntent);
- if (alarmManager != null) {
- alarmManager.cancel(alarmPendingIntent);
- }
- Intent alarmIntent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- alarmIntent.setPackage(context.getPackageName());
- PendingIntent alarmServicePendingIntent = PendingIntent
- .getService(context, config.getAccountId().hashCode(), alarmIntent,
- flagsAlarmPendingIntent);
- if (alarmManager != null) {
- if (pingFrequency != -1) {
- alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + (pingFrequency
- * Constants.ONE_MIN_IN_MILLIS),
- Constants.ONE_MIN_IN_MILLIS * pingFrequency, alarmServicePendingIntent);
- }
- }
- }
- } catch (JSONException e) {
- Logger.v("Unable to raise background Ping event");
- }
+ long lastTS = baseDatabaseManager.loadDBAdapter(context).getLastUninstallTimestamp();
- }
- return null;
+ if (lastTS == 0 || lastTS > System.currentTimeMillis() - 24 * 60 * 60 * 1000) {
+ try {
+ JSONObject eventObject = new JSONObject();
+ eventObject.put("bk", 1);
+ analyticsManager.sendPingEvent(eventObject);
+ Logger.v(config.getAccountId(), "Pushamp - Successfully completed work request");
+ } catch (JSONException e) {
+ Logger.v("Pushamp - Unable to complete work request");
}
- });
+ }
}
@SuppressLint("MissingPermission")
@RequiresApi(api = VERSION_CODES.LOLLIPOP)
- private void createOrResetJobScheduler(Context context) {
-
- int existingJobId = StorageHelper.getInt(context, Constants.PF_JOB_ID, -1);
- JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
+ private void stopJobScheduler(Context context) {
+ int existingJobId = StorageHelper.getInt(context, PF_JOB_ID, -1);
+ if (existingJobId != -1) {
+// Cancel already running job. Possibly unnecessary
+ JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
+ jobScheduler.cancel(existingJobId);
+ StorageHelper.remove(context, PF_JOB_ID);
+ }
+ }
+ private void createOrResetWorker(boolean isPingFrequencyUpdated) {
//Disable push amp for devices below Api 26
if (VERSION.SDK_INT < VERSION_CODES.O) {
- if (existingJobId >= 0) {//cancel already running job
- jobScheduler.cancel(existingJobId);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, -1);
- }
-
- config.getLogger()
- .debug(config.getAccountId(), "Push Amplification feature is not supported below Oreo");
+ config.getLogger().debug(config.getAccountId(), "Pushamp feature is not supported below Oreo");
return;
}
- if (jobScheduler == null) {
- return;
- }
+ String existingWorkName = StorageHelper.getString(context, PF_WORK_ID, "");
int pingFrequency = getPingFrequency(context);
- if (existingJobId < 0 && pingFrequency < 0) {
- return; //no running job and nothing to create
- }
-
- if (pingFrequency < 0) { //running job but hard cancel
- jobScheduler.cancel(existingJobId);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, -1);
+ // no running work and nothing to create
+ if (existingWorkName.equals("") && pingFrequency <= 0) {
+ config.getLogger()
+ .debug(config.getAccountId(), "Pushamp - There is no running work and nothing to create");
return;
}
- ComponentName componentName = new ComponentName(context, CTBackgroundJobService.class);
- boolean needsCreate = (existingJobId < 0 && pingFrequency > 0);
-
- //running job, no hard cancel so check for diff in ping frequency and recreate if needed
- JobInfo existingJobInfo = getJobInfo(existingJobId, jobScheduler);
- if (existingJobInfo != null
- && existingJobInfo.getIntervalMillis() != pingFrequency * Constants.ONE_MIN_IN_MILLIS) {
- jobScheduler.cancel(existingJobId);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, -1);
- needsCreate = true;
+ // Running work exists but hard cancel
+ if (pingFrequency <= 0) {
+ config.getLogger()
+ .debug(config.getAccountId(), "Pushamp - Cancelling worker as pingFrequency <=0 ");
+ stopWorker();
+ return;
}
- if (needsCreate) {
- int jobid = config.getAccountId().hashCode();
- JobInfo.Builder builder = new JobInfo.Builder(jobid, componentName);
- builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
- builder.setRequiresCharging(false);
-
- builder.setPeriodic(pingFrequency * Constants.ONE_MIN_IN_MILLIS, 5 * Constants.ONE_MIN_IN_MILLIS);
- builder.setRequiresBatteryNotLow(true);
-
- if (Utils.hasPermission(context, "android.permission.RECEIVE_BOOT_COMPLETED")) {
- builder.setPersisted(true);
+ try {
+ WorkManager workManager = WorkManager.getInstance(context);
+
+ // Create a work request only when it doesn't exist already or the ping frequency is updated
+ if (existingWorkName.equals("") || isPingFrequencyUpdated) {
+ Constraints constraints = new Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .setRequiresCharging(false)
+ .setRequiresBatteryNotLow(true)
+ .build();
+
+ PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(CTPushAmpWorker.class, pingFrequency,
+ TimeUnit.MINUTES, DEFAULT_FLEX_INTERVAL, TimeUnit.MINUTES)
+ .setConstraints(constraints)
+ .build();
+
+ String workName = existingWorkName.equals("") ? config.getAccountId() : existingWorkName;
+
+ workManager.enqueueUniquePeriodicWork(workName, ExistingPeriodicWorkPolicy.REPLACE, request);
+ StorageHelper.putString(context, PF_WORK_ID, workName);
+ config.getLogger().debug(config.getAccountId(),
+ "Pushamp - Finished scheduling periodic work request - " + workName + " with repeatInterval- "
+ + pingFrequency + " minutes");
}
+ } catch (Exception e) {
+ config.getLogger().debug(config.getAccountId(),
+ "Pushamp - Failed scheduling/cancelling periodic work request" + e);
+ }
+ }
- JobInfo jobInfo = builder.build();
- int resultCode = jobScheduler.schedule(jobInfo);
- if (resultCode == JobScheduler.RESULT_SUCCESS) {
- Logger.d(config.getAccountId(), "Job scheduled - " + jobid);
- StorageHelper.putInt(context, Constants.PF_JOB_ID, jobid);
- } else {
- Logger.d(config.getAccountId(), "Job not scheduled - " + jobid);
+ private void stopWorker() {
+ String existingWorkName = StorageHelper.getString(context, PF_WORK_ID, "");
+ if (!existingWorkName.equals("")) {
+ try {
+ WorkManager workManager = WorkManager.getInstance(context);
+ workManager.cancelUniqueWork(existingWorkName);
+ StorageHelper.putString(context, PF_WORK_ID, "");
+ config.getLogger().debug(config.getAccountId(),
+ "Pushamp - Successfully cancelled work");
+ } catch (Exception e) {
+ config.getLogger().debug(config.getAccountId(),
+ "Pushamp - Failure while cancelling work");
}
}
}
@@ -741,8 +717,8 @@ private void findEnabledPushTypes() {
}
private int getPingFrequency(Context context) {
- return StorageHelper.getInt(context, Constants.PING_FREQUENCY,
- Constants.PING_FREQUENCY_VALUE); //intentional global key because only one Job is running
+ return StorageHelper.getInt(context, PING_FREQUENCY,
+ PING_FREQUENCY_VALUE);
}
/**
@@ -762,21 +738,27 @@ private void init() {
}
private void initPushAmp() {
- if (config.isBackgroundSync() && !config
- .isAnalyticsOnly()) {
- Task task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
- task.execute("createOrResetJobScheduler", new Callable() {
- @Override
- public Void call() {
- if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- createOrResetJobScheduler(context);
- } else {
- createAlarmScheduler(context);
- }
- return null;
+ // Prevent PushAmp initialisation on xiaomi's thread
+ if(!Utils.isMainProcess(context, context.getPackageName()))
+ return;
+
+ Task task = CTExecutorFactory.executors(config).postAsyncSafelyTask();
+ task.execute("createOrResetWorker", new Callable() {
+ @Override
+ public Void call() {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ stopJobScheduler(context);
}
- });
- }
+ if (config.isBackgroundSync() && !config.isAnalyticsOnly()) {
+ createOrResetWorker(false);
+ } else {
+ config.getLogger()
+ .debug(config.getAccountId(), "Pushamp - Cancelling worker as background sync is disabled or config is analytics only");
+ stopWorker();
+ }
+ return null;
+ }
+ });
}
private boolean isTimeBetweenDNDTime(Date startTime, Date stopTime, Date currentTime) {
@@ -831,10 +813,7 @@ private boolean isValid(CTPushProvider provider) {
return true;
}
- private Date parseTimeToDate(String time) {
-
- final String inputFormat = "HH:mm";
- SimpleDateFormat inputParser = new SimpleDateFormat(inputFormat, Locale.US);
+ private Date parseTimeToDate(String time, SimpleDateFormat inputParser) {
try {
return inputParser.parse(time);
} catch (java.text.ParseException e) {
@@ -916,54 +895,8 @@ private void registerToken(String token, PushType pushType) {
cacheToken(token, pushType);
}
- private void resetAlarmScheduler(Context context) {
- if (getPingFrequency(context) <= 0) {
- stopAlarmScheduler(context);
- } else {
- stopAlarmScheduler(context);
- createAlarmScheduler(context);
- }
- }
-
private void setPingFrequency(Context context, int pingFrequency) {
- StorageHelper.putInt(context, Constants.PING_FREQUENCY, pingFrequency);
- }
-
- private void createAlarmScheduler(Context context) {
- int pingFrequency = getPingFrequency(context);
- if (pingFrequency > 0) {
- AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- intent.setPackage(context.getPackageName());
-
- int flagsAlarmPendingIntent = PendingIntent.FLAG_UPDATE_CURRENT;
- if (VERSION.SDK_INT >= VERSION_CODES.M) {
- flagsAlarmPendingIntent |= PendingIntent.FLAG_IMMUTABLE;
- }
- PendingIntent alarmPendingIntent = PendingIntent
- .getService(context, config.getAccountId().hashCode(), intent,
- flagsAlarmPendingIntent);
- if (alarmManager != null) {
- alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(),
- Constants.ONE_MIN_IN_MILLIS * pingFrequency, alarmPendingIntent);
- }
- }
- }
-
- private void stopAlarmScheduler(Context context) {
- AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- Intent cancelIntent = new Intent(CTBackgroundIntentService.MAIN_ACTION);
- cancelIntent.setPackage(context.getPackageName());
- int flagsAlarmPendingIntent = PendingIntent.FLAG_UPDATE_CURRENT;
- if (VERSION.SDK_INT >= VERSION_CODES.M) {
- flagsAlarmPendingIntent |= PendingIntent.FLAG_IMMUTABLE;
- }
- PendingIntent alarmPendingIntent = PendingIntent
- .getService(context, config.getAccountId().hashCode(), cancelIntent,
- flagsAlarmPendingIntent);
- if (alarmManager != null && alarmPendingIntent != null) {
- alarmManager.cancel(alarmPendingIntent);
- }
+ StorageHelper.putInt(context, PING_FREQUENCY, pingFrequency);
}
@RestrictTo(Scope.LIBRARY)
@@ -978,16 +911,6 @@ Object getPushRenderingLock() {
return pushRenderingLock;
}
- @RequiresApi(api = VERSION_CODES.LOLLIPOP)
- private static JobInfo getJobInfo(int jobId, JobScheduler jobScheduler) {
- for (JobInfo jobInfo : jobScheduler.getAllPendingJobs()) {
- if (jobInfo.getId() == jobId) {
- return jobInfo;
- }
- }
- return null;
- }
-
@RestrictTo(Scope.LIBRARY)
public void setPushNotificationRenderer(@NonNull INotificationRenderer iNotificationRenderer) {
this.iNotificationRenderer = iNotificationRenderer;
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentService.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentService.java
deleted file mode 100644
index 3eedd1e13..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentService.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.clevertap.android.sdk.pushnotification.amp;
-
-import android.app.IntentService;
-import android.content.Intent;
-import com.clevertap.android.sdk.CleverTapAPI;
-
-
-/**
- * Background Intent Service to sync up for new notifications
- */
-public class CTBackgroundIntentService extends IntentService {
-
- public final static String MAIN_ACTION = "com.clevertap.BG_EVENT";
-
- /**
- * Creates an IntentService. Invoked by your subclass's constructor.
- */
- public CTBackgroundIntentService() {
- super("CTBackgroundIntentService");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- CleverTapAPI.runBackgroundIntentService(getApplicationContext());
- }
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobService.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobService.java
deleted file mode 100644
index 9250b2ce1..000000000
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.clevertap.android.sdk.pushnotification.amp;
-
-import android.app.job.JobParameters;
-import android.app.job.JobService;
-import android.os.Build;
-import androidx.annotation.RequiresApi;
-import com.clevertap.android.sdk.CleverTapAPI;
-import com.clevertap.android.sdk.Logger;
-
-/**
- * Background Job service to sync up for new notifications
- */
-@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
-public class CTBackgroundJobService extends JobService {
-
- @Override
- public boolean onStartJob(final JobParameters params) {
- Logger.v("Job Service is starting");
- new Thread(new Runnable() {
- @Override
- public void run() {
- CleverTapAPI.runJobWork(getApplicationContext(), params);
- jobFinished(params, true);
- }
- }).start();
- return true;
- }
-
- @Override
- public boolean onStopJob(JobParameters params) {
- return true; //to ensure reschedule
- }
-
-}
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTPushAmpWorker.kt b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTPushAmpWorker.kt
new file mode 100644
index 000000000..d5478bae7
--- /dev/null
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/amp/CTPushAmpWorker.kt
@@ -0,0 +1,16 @@
+package com.clevertap.android.sdk.pushnotification.amp
+
+import android.content.Context
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import com.clevertap.android.sdk.CleverTapAPI
+import com.clevertap.android.sdk.Logger
+
+class CTPushAmpWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
+
+ override fun doWork(): Result {
+ Logger.v("PushAmpWorker is awake")
+ CleverTapAPI.runJobWork(applicationContext)
+ return Result.success()
+ }
+}
\ No newline at end of file
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/validation/ManifestValidator.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/validation/ManifestValidator.java
index dfccc9a9e..91218cb62 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/validation/ManifestValidator.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/validation/ManifestValidator.java
@@ -19,8 +19,6 @@
import com.clevertap.android.sdk.pushnotification.CTPushNotificationReceiver;
import com.clevertap.android.sdk.pushnotification.PushConstants.PushType;
import com.clevertap.android.sdk.pushnotification.PushProviders;
-import com.clevertap.android.sdk.pushnotification.amp.CTBackgroundIntentService;
-import com.clevertap.android.sdk.pushnotification.amp.CTBackgroundJobService;
import java.util.ArrayList;
@@ -74,12 +72,6 @@ private static void checkReceiversServices(final Context context, PushProviders
"com.clevertap.android.geofence.CTLocationUpdateReceiver");
validateReceiverInManifest((Application) context.getApplicationContext(),
"com.clevertap.android.geofence.CTGeofenceBootReceiver");
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
- validateServiceInManifest((Application) context.getApplicationContext(),
- CTBackgroundJobService.class.getName());
- }
- validateServiceInManifest((Application) context.getApplicationContext(),
- CTBackgroundIntentService.class.getName());
} catch (Exception e) {
Logger.v("Receiver/Service issue : " + e.toString());
}
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt
index 3d0e6878b..50f439dc3 100644
--- a/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt
+++ b/clevertap-core/src/test/java/com/clevertap/android/sdk/network/api/CtApiTest.kt
@@ -5,11 +5,13 @@ import org.junit.*
import org.junit.runner.*
import org.mockito.*
import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [33])
class CtApiTest {
private lateinit var ctApi: CtApi
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentServiceTest.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentServiceTest.kt
deleted file mode 100644
index 3186091e1..000000000
--- a/clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundIntentServiceTest.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.clevertap.android.sdk.pushnotification.amp
-
-import android.content.Intent
-import com.clevertap.android.sdk.CleverTapAPI
-import com.clevertap.android.shared.test.BaseTestCase
-import com.clevertap.android.shared.test.TestApplication
-import org.junit.*
-import org.junit.Assert.*
-import org.junit.runner.*
-import org.mockito.Mockito.*
-import org.robolectric.Robolectric
-import org.robolectric.RobolectricTestRunner
-import org.robolectric.annotation.Config
-
-@RunWith(RobolectricTestRunner::class)
-@Config(sdk = [28], application = TestApplication::class)
-class CTBackgroundIntentServiceTest : BaseTestCase() {
-
- override fun setUp() {
- super.setUp()
- }
-
- @Test
- fun test_handleIntent() {
- mockStatic(CleverTapAPI::class.java).use {
- val exceptionMessage = "CleverTapAPI#runBackgroundIntentService called"
- `when`(CleverTapAPI.runBackgroundIntentService(any())).thenThrow(RuntimeException(exceptionMessage))
-
- val exception = assertThrows(RuntimeException::class.java) {
- val serviceController = Robolectric.buildIntentService(CTBackgroundIntentService::class.java, Intent())
- serviceController.create().handleIntent()
- }
-
- assertTrue(exceptionMessage.equals(exception.message))
- }
- }
-}
\ No newline at end of file
diff --git a/clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobServiceTest.kt b/clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobServiceTest.kt
deleted file mode 100644
index 7733ccc83..000000000
--- a/clevertap-core/src/test/java/com/clevertap/android/sdk/pushnotification/amp/CTBackgroundJobServiceTest.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.clevertap.android.sdk.pushnotification.amp
-
-import android.app.job.JobParameters
-import com.clevertap.android.shared.test.BaseTestCase
-import com.clevertap.android.shared.test.TestApplication
-import org.junit.*
-import org.junit.runner.*
-import org.mockito.Mockito.*
-import org.robolectric.Robolectric
-import org.robolectric.RobolectricTestRunner
-import org.robolectric.annotation.Config
-
-@RunWith(RobolectricTestRunner::class)
-@Config(sdk = [28], application = TestApplication::class)
-class CTBackgroundJobServiceTest : BaseTestCase() {
-
- private lateinit var service: CTBackgroundJobService
- private lateinit var mockParams: JobParameters
- override fun setUp() {
- super.setUp()
- service = spy(Robolectric.setupService(CTBackgroundJobService::class.java))
- mockParams = mock(JobParameters::class.java)
- }
-
- @Test
- fun test_onStartJob() {
-
- }
-}
\ No newline at end of file
diff --git a/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/TemplateRenderer.kt b/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/TemplateRenderer.kt
index ffc3c95d2..8ad8a3d3a 100644
--- a/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/TemplateRenderer.kt
+++ b/clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/TemplateRenderer.kt
@@ -1,5 +1,6 @@
package com.clevertap.android.pushtemplates
+import android.app.ActivityOptions
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.ContentResolver
@@ -453,7 +454,7 @@ class TemplateRenderer : INotificationRenderer, AudibleNotification {
extras: Bundle,
notificationId: Int,
nb: Builder, actions: JSONArray?
- ): Builder? {
+ ): Builder {
val intentServiceName = ManifestInfo.getInstance(context).intentServiceName
var clazz: Class<*>? = null
if (intentServiceName != null) {
@@ -563,9 +564,15 @@ class TemplateRenderer : INotificationRenderer, AudibleNotification {
actionLaunchIntent!!, flagsActionLaunchPendingIntent
)
} else {
+ var optionsBundle: Bundle? = null
+ if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ optionsBundle = ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ ).toBundle()
+ }
PendingIntent.getActivity(
context, requestCode,
- actionLaunchIntent, flagsActionLaunchPendingIntent
+ actionLaunchIntent!!, flagsActionLaunchPendingIntent, optionsBundle
)
}
nb.addAction(icon, label, actionIntent)
diff --git a/gradle-scripts/commons.gradle b/gradle-scripts/commons.gradle
index d6bc660c6..88c5edd6a 100644
--- a/gradle-scripts/commons.gradle
+++ b/gradle-scripts/commons.gradle
@@ -31,12 +31,10 @@ group = publishedGroupId
def (major,minor,patch) = libraryVersion.split("\\.")
android {
- compileSdkVersion libs.versions.android.compileSdk.get().toInteger()
- buildToolsVersion libs.versions.android.buildTools.get()
+ compileSdk libs.versions.android.compileSdk.get().toInteger()
defaultConfig {
minSdkVersion libs.versions.android.minSdk.get().toInteger()
- targetSdkVersion libs.versions.android.targetSdk.get().toInteger()
versionCode "${major}0${minor}0${patch}".toInteger()
versionName libraryVersion
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 257652497..dab3c2c20 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,12 +1,10 @@
[versions]
# Project
-android_compileSdk = "33"
+android_compileSdk = "34"
android_gradle_plugin = "7.4.2"
android_minSdk = "19"
-android_targetSdk = "33"
kotlin_plugin = "1.7.20"
sonarqube_plugin = "3.3"
-android_buildTools = "33.0.0"
detekt_gradle_plugin = "1.20.0-RC1"
firebase_gradle_crashlytics = "2.8.1"
diff --git a/sample/build.gradle b/sample/build.gradle
index 6ca29426b..d93d0be5f 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -13,11 +13,11 @@ allprojects {
}
android {
- compileSdkVersion 33
+ compileSdk 34
defaultConfig {
applicationId "com.clevertap.demo"
minSdkVersion 21
- targetSdkVersion 33
+ targetSdkVersion 34
versionCode 200005
versionName "1.6.2-full"
multiDexEnabled true
diff --git a/test_shared/build.gradle b/test_shared/build.gradle
index 241e1a06b..612edf81c 100644
--- a/test_shared/build.gradle
+++ b/test_shared/build.gradle
@@ -4,12 +4,10 @@ plugins {
}
android {
- compileSdkVersion libs.versions.android.compileSdk.get().toInteger()
- buildToolsVersion libs.versions.android.buildTools.get()
+ compileSdk libs.versions.android.compileSdk.get().toInteger()
defaultConfig {
minSdkVersion libs.versions.android.minSdk.get().toInteger()
- targetSdkVersion libs.versions.android.targetSdk.get().toInteger()
versionCode 1
versionName "1.0"
From c6db1f5e7c5a983c81d0593adfcce1bcfe996919 Mon Sep 17 00:00:00 2001
From: anush
Date: Fri, 16 Feb 2024 12:07:56 +0530
Subject: [PATCH 20/29] task(SDK-3620) - Updates build.gradle for test_shared
module
---
test_shared/build.gradle | 3 ---
1 file changed, 3 deletions(-)
diff --git a/test_shared/build.gradle b/test_shared/build.gradle
index 6ac892c3d..8cb37eacd 100644
--- a/test_shared/build.gradle
+++ b/test_shared/build.gradle
@@ -8,9 +8,6 @@ android {
defaultConfig {
minSdkVersion libs.versions.android.minSdk.get().toInteger()
- targetSdkVersion libs.versions.android.targetSdk.get().toInteger()
- versionCode 1
- versionName "1.0"
// testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
From a487eb7002b93bec8a9a249ab1b8c4ec4dd2e084 Mon Sep 17 00:00:00 2001
From: CTLalit <144685420+CTLalit@users.noreply.github.com>
Date: Fri, 16 Feb 2024 12:27:15 +0530
Subject: [PATCH 21/29] Feat: Deprecated labels for xiaomi methods (#553)
- xiaomi will be removed after March according to compliance
---
.../src/main/java/com/clevertap/android/sdk/CleverTapAPI.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
index 48056822e..7ee4572d8 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
@@ -223,6 +223,7 @@ public static void changeCredentials(String accountID, String token, String prox
* @param xiaomiAppID Xiaomi App Id
* @param xiaomiAppKey Xiaomi App Key
*/
+ @Deprecated
public static void changeXiaomiCredentials(String xiaomiAppID, String xiaomiAppKey) {
ManifestInfo.changeXiaomiCredentials(xiaomiAppID, xiaomiAppKey);
}
@@ -2364,6 +2365,7 @@ public void pushProfile(final Map profile) {
* and false to not receive any messages from CleverTap.
*/
@SuppressWarnings("unused")
+ @Deprecated
public void pushXiaomiRegistrationId(String regId,@NonNull String region, boolean register) {
if(TextUtils.isEmpty(region)){
Logger.d("CleverTapApi : region must not be null or empty , use MiPushClient.getAppRegion(context) to provide appropriate region");
@@ -3135,6 +3137,7 @@ public void renderPushNotificationOnCallerThread(@NonNull INotificationRenderer
* 2. {@link PushConstants#XIAOMI_MIUI_DEVICES} (int value = 2)
* 3. {@link PushConstants#NO_DEVICES} (int value = 3)
*/
+ @Deprecated
public static void enableXiaomiPushOn(@XiaomiPush int xpsRunningDevices) {
PushType.XPS.setRunningDevices(xpsRunningDevices);
}
@@ -3146,6 +3149,7 @@ public static void enableXiaomiPushOn(@XiaomiPush int xpsRunningDevices) {
* 2. {@link XiaomiPush#XIAOMI_MIUI_DEVICES} (int value = 2)
* 3. {@link XiaomiPush#NO_DEVICES} (int value = 3)
*/
+ @Deprecated
public static @XiaomiPush int getEnableXiaomiPushOn() {
return PushType.XPS.getRunningDevices();
}
From 427129ac5a5396557feed8c6bb7f5224910b6406 Mon Sep 17 00:00:00 2001
From: CTLalit <144685420+CTLalit@users.noreply.github.com>
Date: Fri, 16 Feb 2024 12:41:09 +0530
Subject: [PATCH 22/29] Workflow: Git actions for static checks (#555)
---
.github/workflows/on_pr_from_task_to_develop.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/on_pr_from_task_to_develop.yml b/.github/workflows/on_pr_from_task_to_develop.yml
index 497a1b6a8..82f8f0480 100644
--- a/.github/workflows/on_pr_from_task_to_develop.yml
+++ b/.github/workflows/on_pr_from_task_to_develop.yml
@@ -5,7 +5,7 @@ on:
jobs:
lint-staticChecks-test-build:
- if: startsWith(github.head_ref, 'task/')
+ if: startsWith(github.head_ref, 'task/') || startsWith(github.head_ref, 'feat/') || startsWith(github.head_ref, 'bug/')
runs-on: ubuntu-latest
permissions:
contents: read
From f7edd03fe4d97c4a624a2b499b2e07e350978155 Mon Sep 17 00:00:00 2001
From: CTLalit <144685420+CTLalit@users.noreply.github.com>
Date: Fri, 16 Feb 2024 13:08:57 +0530
Subject: [PATCH 23/29] Task/sdk 3659/accessibility ids (#556)
* task(SDK-3659) - Adds accessibility ids for inapp title, message, bgimg and btns
* task(SDK-3659) - Adds accessibility ids for inapp icons
---------
Co-authored-by: anush
---
clevertap-core/src/main/res/layout-land/inapp_cover.xml | 6 ++++++
.../src/main/res/layout-land/inapp_cover_image.xml | 1 +
clevertap-core/src/main/res/layout-land/inapp_footer.xml | 5 +++++
.../src/main/res/layout-land/inapp_half_interstitial.xml | 6 ++++++
.../main/res/layout-land/inapp_half_interstitial_image.xml | 1 +
clevertap-core/src/main/res/layout-land/inapp_header.xml | 5 +++++
.../src/main/res/layout-land/inapp_interstitial.xml | 6 ++++++
.../src/main/res/layout-land/inapp_interstitial_image.xml | 1 +
.../src/main/res/layout-sw600dp-land/inapp_cover.xml | 6 ++++++
.../src/main/res/layout-sw600dp-land/inapp_cover_image.xml | 1 +
.../res/layout-sw600dp-land/tab_inapp_half_interstitial.xml | 6 ++++++
.../tab_inapp_half_interstitial_image.xml | 1 +
.../main/res/layout-sw600dp-land/tab_inapp_interstitial.xml | 6 ++++++
.../layout-sw600dp-land/tab_inapp_interstitial_image.xml | 1 +
clevertap-core/src/main/res/layout-sw600dp/inapp_cover.xml | 6 ++++++
.../src/main/res/layout-sw600dp/inapp_cover_image.xml | 1 +
.../main/res/layout-sw600dp/tab_inapp_half_interstitial.xml | 6 ++++++
.../layout-sw600dp/tab_inapp_half_interstitial_image.xml | 1 +
.../src/main/res/layout-sw600dp/tab_inapp_interstitial.xml | 6 ++++++
.../res/layout-sw600dp/tab_inapp_interstitial_image.xml | 1 +
clevertap-core/src/main/res/layout/inapp_cover.xml | 6 ++++++
clevertap-core/src/main/res/layout/inapp_cover_image.xml | 1 +
clevertap-core/src/main/res/layout/inapp_footer.xml | 5 +++++
.../src/main/res/layout/inapp_half_interstitial.xml | 6 ++++++
.../src/main/res/layout/inapp_half_interstitial_image.xml | 1 +
clevertap-core/src/main/res/layout/inapp_header.xml | 5 +++++
clevertap-core/src/main/res/layout/inapp_interstitial.xml | 6 ++++++
.../src/main/res/layout/inapp_interstitial_image.xml | 1 +
28 files changed, 104 insertions(+)
diff --git a/clevertap-core/src/main/res/layout-land/inapp_cover.xml b/clevertap-core/src/main/res/layout-land/inapp_cover.xml
index 04e34644b..121107e6b 100644
--- a/clevertap-core/src/main/res/layout-land/inapp_cover.xml
+++ b/clevertap-core/src/main/res/layout-land/inapp_cover.xml
@@ -12,6 +12,7 @@
Date: Fri, 16 Feb 2024 19:05:44 +0530
Subject: [PATCH 24/29] task(SDK-3620) - Updates AGP to 8.2.2 (#557)
* task(SDK-3620) - Updates libs.versions.toml
* task(SDK-3620) - Updates libs.versions.toml for kotlin_lpugin
---
gradle/libs.versions.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index f64dd253f..d5966c3cb 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,7 +1,7 @@
[versions]
# Project
android_compileSdk = "34"
-android_gradle_plugin = "7.4.2"
+android_gradle_plugin = "8.2.2"
android_minSdk = "19"
kotlin_plugin = "1.9.0"
sonarqube_plugin = "3.3"
From 59380c94a7626dc30ca1efd3a652321e91245d55 Mon Sep 17 00:00:00 2001
From: CTLalit <144685420+CTLalit@users.noreply.github.com>
Date: Wed, 21 Feb 2024 15:35:55 +0530
Subject: [PATCH 25/29] Task/kotlin version (#559)
* Feat: Removal of databinding
- it was creating problems for sample app
- kotlin version 1920 is required for databinding so we were forced
* Feat: downgrading kotlin compilation version
- downgrading kotlin compoilation version
- it was forcing client to upgrade kotlin version in their project
- "org.jetbrains.kotlin:kotlin-gradle-plugin" impacted
---
gradle/libs.versions.toml | 2 +-
sample/build.gradle | 1 -
.../demo/ui/main/HomeScreenFragment.kt | 32 +++---
.../demo/ui/main/HomeScreenListAdapter.kt | 68 +++++++------
.../main/res/layout/ct_feature_functions.xml | 57 +++--------
sample/src/main/res/layout/ct_feature_row.xml | 48 ++++-----
.../main/res/layout/home_screen_fragment.xml | 99 +++++++++----------
7 files changed, 133 insertions(+), 174 deletions(-)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d5966c3cb..72401cbbc 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -3,7 +3,7 @@
android_compileSdk = "34"
android_gradle_plugin = "8.2.2"
android_minSdk = "19"
-kotlin_plugin = "1.9.0"
+kotlin_plugin = "1.7.20"
sonarqube_plugin = "3.3"
android_buildTools = "34.0.0"
detekt_gradle_plugin = "1.20.0-RC1"
diff --git a/sample/build.gradle b/sample/build.gradle
index 8bff0f0f1..8f9026b98 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -60,7 +60,6 @@ android {
}
}
buildFeatures {
- dataBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
diff --git a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenFragment.kt b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenFragment.kt
index 7b0798ffe..1cf1e76ee 100644
--- a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenFragment.kt
+++ b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenFragment.kt
@@ -10,11 +10,12 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
-import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.ExpandableListView
import android.widget.Toast
+import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
@@ -30,13 +31,17 @@ import com.clevertap.demo.R
import com.clevertap.demo.ViewModelFactory
import com.clevertap.demo.WebViewActivity
import com.clevertap.demo.action
-import com.clevertap.demo.databinding.HomeScreenFragmentBinding
import com.clevertap.demo.snack
import org.json.JSONObject
private const val TAG = "HomeScreenFragment"
private const val PERMISSIONS_REQUEST_CODE = 34
+data class HomeScreenFragmentBinding(
+ val expandableListView: ExpandableListView,
+ val root: CoordinatorLayout
+)
+
class HomeScreenFragment : Fragment() {
private val viewModel by viewModels {
@@ -44,7 +49,6 @@ class HomeScreenFragment : Fragment() {
}
companion object {
-
fun newInstance() = HomeScreenFragment()
}
@@ -54,12 +58,15 @@ class HomeScreenFragment : Fragment() {
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
- listItemBinding = HomeScreenFragmentBinding.inflate(layoutInflater, container, false).apply {
- viewmodel = viewModel
- }
+ val view = LayoutInflater.from(context).inflate(R.layout.home_screen_fragment, container, false)
+ listItemBinding = HomeScreenFragmentBinding(
+ expandableListView = view.findViewById(R.id.expandableListView),
+ root = view.findViewById(R.id.home_root)
+ )
+
listItemBinding.expandableListView.isNestedScrollingEnabled = true
- return listItemBinding.root
+ return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -95,14 +102,9 @@ class HomeScreenFragment : Fragment() {
}
private fun setupListAdapter() {
- val viewModel = listItemBinding.viewmodel
- if (viewModel != null) {
- val listAdapter =
- HomeScreenListAdapter(viewModel, HomeScreenModel.listData.keys.toList(), HomeScreenModel.listData)
- listItemBinding.expandableListView.setAdapter(listAdapter)
- } else {
- Log.w(TAG, "ViewModel not initialized when attempting to set up adapter.")
- }
+ listItemBinding.expandableListView.setAdapter(
+ HomeScreenListAdapter(viewModel, HomeScreenModel.listData.keys.toList(), HomeScreenModel.listData)
+ )
}
private fun initCTGeofenceApi(cleverTapInstance: CleverTapAPI) {
diff --git a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenListAdapter.kt b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenListAdapter.kt
index 76ad0c7c4..06f5ec735 100644
--- a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenListAdapter.kt
+++ b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenListAdapter.kt
@@ -4,11 +4,19 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseExpandableListAdapter
-import com.clevertap.demo.databinding.CtFeatureFunctionsBinding
-import com.clevertap.demo.databinding.CtFeatureRowBinding
+import android.widget.TextView
+import com.clevertap.demo.R
+data class CtFeatureRowBinding(
+ val title: TextView
+)
+
+data class CtFeatureFunctionsBinding(
+ val fTitle: TextView
+)
class HomeScreenListAdapter(
- private val viewModel: HomeScreenViewModel, private val titleList: List,
+ private val viewModel: HomeScreenViewModel,
+ private val titleList: List,
private val detailsList: Map>
) : BaseExpandableListAdapter() {
@@ -19,21 +27,18 @@ class HomeScreenListAdapter(
override fun hasStableIds(): Boolean = false
override fun getGroupView(groupPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup?): View {
- var convertViewShadow = convertView
- val binding: CtFeatureRowBinding
-
- if (convertViewShadow == null) {
- val layoutInflater = LayoutInflater.from(parent?.context)
- binding = CtFeatureRowBinding.inflate(layoutInflater, parent, false)
- convertViewShadow = binding.root
- } else {
- binding = convertViewShadow.tag as CtFeatureRowBinding
- }
- binding.title = getGroup(groupPosition) as String
+ val view = convertView
+ ?: LayoutInflater.from(parent?.context).inflate(R.layout.ct_feature_row, parent, false).also { view ->
+ val binding = CtFeatureRowBinding(
+ title = view.findViewById(R.id.featureTitle)
+ )
+ view.tag = binding
+ }
+
+ (view.tag as? CtFeatureRowBinding)?.title?.text = getGroup(groupPosition) as String
- convertViewShadow.tag = binding
- return convertViewShadow
+ return view
}
override fun getChildrenCount(groupPosition: Int): Int {
@@ -53,24 +58,23 @@ class HomeScreenListAdapter(
convertView: View?,
parent: ViewGroup?
): View {
- var convertViewShadow = convertView
- val binding: CtFeatureFunctionsBinding
-
- if (convertViewShadow == null) {
- val layoutInflater = LayoutInflater.from(parent?.context)
- binding = CtFeatureFunctionsBinding.inflate(layoutInflater, parent, false)
- convertViewShadow = binding.root
- } else {
- binding = convertViewShadow.tag as CtFeatureFunctionsBinding
- }
- binding.fTitle = getChild(groupPosition, childPosition) as String
- binding.groupPosition = groupPosition
- binding.childPosition = childPosition
- binding.viewmodel = viewModel
+ val view = convertView
+ ?: LayoutInflater.from(parent?.context).inflate(R.layout.ct_feature_functions, parent, false).also { view ->
+ val binding = CtFeatureFunctionsBinding(
+ fTitle = view.findViewById(R.id.functionTitle)
+ )
+ view.tag = binding
+ }
+ (view.tag as? CtFeatureFunctionsBinding)?.fTitle?.apply {
+ text = getChild(groupPosition, childPosition) as String
+ setOnClickListener(null)
+ setOnClickListener {
+ viewModel.onChildClick(groupPosition = groupPosition, childPosition = childPosition)
+ }
+ }
- convertViewShadow.tag = binding
- return convertViewShadow
+ return view
}
override fun getChildId(groupPosition: Int, childPosition: Int): Long = childPosition.toLong()
diff --git a/sample/src/main/res/layout/ct_feature_functions.xml b/sample/src/main/res/layout/ct_feature_functions.xml
index f2d550d02..ef5248078 100644
--- a/sample/src/main/res/layout/ct_feature_functions.xml
+++ b/sample/src/main/res/layout/ct_feature_functions.xml
@@ -1,45 +1,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingBottom="12dp"
+ android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
+ android:paddingStart="?android:attr/expandableListPreferredChildPaddingLeft"
+ android:paddingTop="12dp"
+ android:textColor="@android:color/black"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
\ No newline at end of file
diff --git a/sample/src/main/res/layout/ct_feature_row.xml b/sample/src/main/res/layout/ct_feature_row.xml
index d04b1eb8a..011a9a88d 100644
--- a/sample/src/main/res/layout/ct_feature_row.xml
+++ b/sample/src/main/res/layout/ct_feature_row.xml
@@ -1,33 +1,21 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:layout_height="wrap_content"
+ android:background="#757de8"
+ android:paddingStart="?android:attr/expandableListPreferredItemPaddingLeft"
+ android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
+ android:paddingTop="15dp"
+ android:paddingBottom="15dp"
+ android:textColor="@android:color/white"
+ android:textStyle="bold"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
\ No newline at end of file
diff --git a/sample/src/main/res/layout/home_screen_fragment.xml b/sample/src/main/res/layout/home_screen_fragment.xml
index 066854c42..5e30b6f19 100644
--- a/sample/src/main/res/layout/home_screen_fragment.xml
+++ b/sample/src/main/res/layout/home_screen_fragment.xml
@@ -1,71 +1,62 @@
-
-
-
-
-
-
-
-
+
+
+ android:layout_height="240dp"
+ android:fitsSystemWindows="true"
+ android:theme="@style/AppTheme.AppBarOverlay">
-
+ app:contentScrim="?attr/colorPrimary"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed"
+ app:toolbarId="@+id/toolbar">
-
+ android:scaleType="fitCenter"
+ android:src="@drawable/logo"
+ app:layout_collapseMode="parallax" />
-
+
-
+
-
+
-
+
-
-
-
-
-
-
\ No newline at end of file
+ android:divider="@android:color/white"
+ android:dividerHeight="2dp"
+ android:indicatorLeft="?android:attr/expandableListPreferredItemIndicatorLeft" />
+
+
\ No newline at end of file
From 3317fa7603e80819383a25168fae66f731269f9e Mon Sep 17 00:00:00 2001
From: CTLalit <144685420+CTLalit@users.noreply.github.com>
Date: Wed, 21 Feb 2024 18:18:39 +0530
Subject: [PATCH 26/29] Docs/release cadence feb21 (#560)
* Changelog: Xiaomi deprecations
- deprecations added in xiaomi docs
* Changelog: Xiaomi deprecations contd...
- deprecations added in xiaomi docs
* Changelog: Core SDK changelog
* Changelog: Core SDK changelog
* Changelog: SDK changelogs
- push templates, geofence and huawei change logs updated.
* Changelog: Core changelog
* Changelog: FAQs updated
* Changelog: Main changelog
- hyperlinks individual ones
* Versioning: SDK versions
- bumped for new release
- did for all but xiaomi sdks.
* Chore: Copy templates
- ran copy templates gradle command.
* docs(SDK-3672) - Minor updates to docs
---------
Co-authored-by: anush
---
CHANGELOG.md | 7 +++++++
README.md | 30 +++++++++++++--------------
docs/CTCORECHANGELOG.md | 26 ++++++++++++++++++++++-
docs/CTGEOFENCE.md | 4 ++--
docs/CTGEOFENCECHANGELOG.md | 4 ++++
docs/CTHUAWEIPUSH.md | 4 ++--
docs/CTHUAWEIPUSHCHANGELOG.md | 4 ++++
docs/CTPUSHTEMPLATES.md | 4 ++--
docs/CTPUSHTEMPLATESCHANGELOG.md | 10 +++++++++
docs/CTXIAOMIPUSH.md | 5 +++++
docs/CTXIAOMIPUSHCHANGELOG.md | 5 +++++
docs/FAQ.md | 6 ++++++
gradle/libs.versions.toml | 8 +++----
templates/CTCORECHANGELOG.md | 26 ++++++++++++++++++++++-
templates/CTGEOFENCECHANGELOG.md | 4 ++++
templates/CTHUAWEIPUSHCHANGELOG.md | 4 ++++
templates/CTPUSHTEMPLATESCHANGELOG.md | 10 +++++++++
templates/CTXIAOMIPUSH.md | 5 +++++
templates/CTXIAOMIPUSHCHANGELOG.md | 5 +++++
templates/FAQ.md | 6 ++++++
20 files changed, 150 insertions(+), 27 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a30418f3f..f73b922ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
## CHANGE LOG.
+### February 21, 2024
+
+* [CleverTap Android SDK v6.1.0](docs/CTCORECHANGELOG.md)
+* [CleverTap Push Templates SDK v1.2.3](docs/CTPUSHTEMPLATESCHANGELOG.md).
+* [CleverTap Geofence SDK v1.3.0](docs/CTGEOFENCECHANGELOG.md)
+* [CleverTap Huawei Push SDK v1.3.4](docs/CTHUAWEIPUSHCHANGELOG.md)
+
### January 15, 2024
* [CleverTap Android SDK v6.0.0](docs/CTCORECHANGELOG.md)
diff --git a/README.md b/README.md
index 137526d51..3973c4f4f 100644
--- a/README.md
+++ b/README.md
@@ -25,17 +25,17 @@ To get started, sign up [here](https://clevertap.com/live-product-demo/)
We publish the SDK to `mavenCentral` as an `AAR` file. Just declare it as dependency in your `build.gradle` file.
```groovy
- dependencies {
- implementation "com.clevertap.android:clevertap-android-sdk:6.0.0"
-}
+ dependencies {
+ implementation "com.clevertap.android:clevertap-android-sdk:6.1.0"
+ }
```
Alternatively, you can download and add the AAR file included in this repo in your Module libs directory and tell gradle to install it like this:
```groovy
- dependencies {
- implementation(name: "clevertap-android-sdk-6.0.0", ext: 'aar')
-}
+ dependencies {
+ implementation (name: "clevertap-android-sdk-6.1.0", ext: 'aar')
+ }
```
@@ -45,9 +45,9 @@ Alternatively, you can download and add the AAR file included in this repo in yo
Add the Firebase Messaging library and Android Support Library v4 as dependencies to your Module `build.gradle` file.
```groovy
- dependencies {
- implementation "com.clevertap.android:clevertap-android-sdk:6.0.0"
- implementation "androidx.core:core:1.9.0"
+ dependencies {
+ implementation "com.clevertap.android:clevertap-android-sdk:6.1.0"
+ implementation "androidx.core:core:1.9.0"
implementation "com.google.firebase:firebase-messaging:23.0.6"
implementation "com.google.android.gms:play-services-ads:22.3.0" // Required only if you enable Google ADID collection in the SDK (turned off by default).
}
@@ -70,8 +70,8 @@ Also be sure to include the `google-services.json` classpath in your Project lev
}
dependencies {
- classpath "com.android.tools.build:gradle:7.4.2"
- classpath "com.google.gms:google-services:4.3.3"
+ classpath "com.android.tools.build:gradle:8.2.2"
+ classpath "com.google.gms:google-services:4.4.0"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -85,11 +85,11 @@ Add your FCM generated `google-services.json` file to your project and add the f
apply plugin: 'com.google.gms.google-services'
```
Interstitial InApp Notification templates support Audio and Video with the help of ExoPlayer. To enable Audio/Video in your Interstitial InApp Notifications, add the following dependencies in your `build.gradle` file :
-
+
```groovy
-implementation "com.google.android.exoplayer:exoplayer:2.19.1"
-implementation "com.google.android.exoplayer:exoplayer-hls:2.19.1"
-implementation "com.google.android.exoplayer:exoplayer-ui:2.19.1"
+ implementation "com.google.android.exoplayer:exoplayer:2.19.1"
+ implementation "com.google.android.exoplayer:exoplayer-hls:2.19.1"
+ implementation "com.google.android.exoplayer:exoplayer-ui:2.19.1"
```
Once you've updated your module `build.gradle` file, make sure you have specified `mavenCentral()` and `google()` as a repositories in your project `build.gradle` and then sync your project in File -> Sync Project with Gradle Files.
diff --git a/docs/CTCORECHANGELOG.md b/docs/CTCORECHANGELOG.md
index 576660b8e..6f1c9419b 100644
--- a/docs/CTCORECHANGELOG.md
+++ b/docs/CTCORECHANGELOG.md
@@ -1,5 +1,29 @@
## CleverTap Android SDK CHANGE LOG
+### Version 6.1.0 (February 21, 2024)
+
+#### New Features
+
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+* Deprecates Xiaomi public methods as we are sunsetting SDK. Details [here](https://dev.mi.com/distribute/doc/details?pId=1555).
+* Adds Accessibility ids for UI components of SDK
+* Migrates JobScheduler to WorkManager for Push Amplification.
+
+#### Breaking API Changes
+
+* **CTPushAmpWorker breaks custom WorkerFactory implementation of an App**:
+ * If you are using custom `WorkFactory` implementation of `WorkManager` then make sure that you
+ correctly handle workers defined by CleverTap SDK and other third party dependencies.
+ * You must return `null` from `createWorker()` for any unknown workerClassName. Please check
+ implementation provided in the
+ blog [here](https://medium.com/androiddevelopers/customizing-workmanager-fundamentals-fdaa17c46dd2)
+
+#### Bug Fixes
+
+* Fixes InApps crash in a rare activity destroyed race condition
+* Fixes Potential ANR in a race condition of SDK initialisation in multithreaded setup
+
### Version 6.0.0 (January 15, 2024)
#### New Features
@@ -104,7 +128,7 @@ Please remove the integrated Rendermax SDK before you upgrade to Android SDK v5.
correctly handle workers defined by CleverTap SDK and other third party dependencies.
* You must return `null` from `createWorker()` for any unknown workerClassName. Please check
implementation provided in the
- bolg [here](https://medium.com/androiddevelopers/customizing-workmanager-fundamentals-fdaa17c46dd2)
+ blog [here](https://medium.com/androiddevelopers/customizing-workmanager-fundamentals-fdaa17c46dd2)
* **Behavioral change of `createNotification` methods**:
* The following APIs now run on the caller's thread. Make sure to call it
diff --git a/docs/CTGEOFENCE.md b/docs/CTGEOFENCE.md
index d6ccebfe8..ca570e08b 100644
--- a/docs/CTGEOFENCE.md
+++ b/docs/CTGEOFENCE.md
@@ -16,8 +16,8 @@ CleverTap Android Geofence SDK provides **Geofencing capabilities** to CleverTap
Add the following dependencies to the `build.gradle`
```Groovy
-implementation "com.clevertap.android:clevertap-geofence-sdk:1.2.0"
-implementation "com.clevertap.android:clevertap-android-sdk:6.0.0" // 3.9.0 and above
+implementation "com.clevertap.android:clevertap-geofence-sdk:1.3.0"
+implementation "com.clevertap.android:clevertap-android-sdk:6.1.0" // 3.9.0 and above
implementation "com.google.android.gms:play-services-location:21.0.0"
implementation "androidx.work:work-runtime:2.7.1" // required for FETCH_LAST_LOCATION_PERIODIC
implementation "androidx.concurrent:concurrent-futures:1.1.0" // required for FETCH_LAST_LOCATION_PERIODIC
diff --git a/docs/CTGEOFENCECHANGELOG.md b/docs/CTGEOFENCECHANGELOG.md
index 9f323230b..7574d31f5 100644
--- a/docs/CTGEOFENCECHANGELOG.md
+++ b/docs/CTGEOFENCECHANGELOG.md
@@ -1,5 +1,9 @@
## CleverTap Geofence SDK CHANGE LOG
+### Version 1.3.0 (February 21, 2024)
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+
### Version 1.2.0 (November 1, 2022)
* Updates [play-services-location](https://developers.google.com/android/guides/releases#october_13_2022) to `v21.0.0`
* Updates [work-runtime](https://developer.android.com/jetpack/androidx/releases/work#2.7.1) to `v2.7.1`
diff --git a/docs/CTHUAWEIPUSH.md b/docs/CTHUAWEIPUSH.md
index ff07f200a..04fe83918 100644
--- a/docs/CTHUAWEIPUSH.md
+++ b/docs/CTHUAWEIPUSH.md
@@ -37,7 +37,7 @@ buildscript {
}
dependencies {
// FOR HUAWEI ADD THIS
- classpath "com.huawei.agconnect:agcp:1.9.0.300"
+ classpath "com.huawei.agconnect:agcp:1.9.1.300"
}
}
@@ -52,7 +52,7 @@ allprojects {
* Add the following to your app’s `build.gradle` file
```groovy
-implementation "com.clevertap.android:clevertap-hms-sdk:1.3.3"
+implementation "com.clevertap.android:clevertap-hms-sdk:1.3.4"
implementation "com.huawei.hms:push:6.11.0.300"
//At the bottom of the file add this
diff --git a/docs/CTHUAWEIPUSHCHANGELOG.md b/docs/CTHUAWEIPUSHCHANGELOG.md
index 561e14626..ef80cd049 100644
--- a/docs/CTHUAWEIPUSHCHANGELOG.md
+++ b/docs/CTHUAWEIPUSHCHANGELOG.md
@@ -1,5 +1,9 @@
## CleverTap Huawei Push SDK CHANGE LOG
+### Version 1.3.4 (February 21, 2024)
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+
### Version 1.3.3 (August 10, 2023)
* Updated Huawei Push SDK to v6.11.0.300
* Supports CleverTap Android SDK v5.2.0
diff --git a/docs/CTPUSHTEMPLATES.md b/docs/CTPUSHTEMPLATES.md
index 06a15fe50..909e3d526 100644
--- a/docs/CTPUSHTEMPLATES.md
+++ b/docs/CTPUSHTEMPLATES.md
@@ -20,8 +20,8 @@ CleverTap Push Templates SDK helps you engage with your users using fancy push n
1. Add the dependencies to the `build.gradle`
```groovy
-implementation "com.clevertap.android:push-templates:1.2.2"
-implementation "com.clevertap.android:clevertap-android-sdk:6.0.0" // 4.4.0 and above
+implementation "com.clevertap.android:push-templates:1.2.3"
+implementation "com.clevertap.android:clevertap-android-sdk:6.1.0" // 4.4.0 and above
```
2. Add the following line to your Application class before the `onCreate()`
diff --git a/docs/CTPUSHTEMPLATESCHANGELOG.md b/docs/CTPUSHTEMPLATESCHANGELOG.md
index 36232615d..9b806cfd6 100644
--- a/docs/CTPUSHTEMPLATESCHANGELOG.md
+++ b/docs/CTPUSHTEMPLATESCHANGELOG.md
@@ -1,5 +1,15 @@
## CleverTap Push Templates SDK CHANGE LOG
+### Version 1.2.3 (February 21, 2024)
+
+#### New features
+
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+
+#### Bug Fixes
+* Fixes [Input Box](https://developer.clevertap.com/docs/push-templates-android#input-box-template) push template.
+
### Version 1.2.2 (January 15, 2024)
* Minor changes and improvements
diff --git a/docs/CTXIAOMIPUSH.md b/docs/CTXIAOMIPUSH.md
index 2da95d497..4825ec496 100644
--- a/docs/CTXIAOMIPUSH.md
+++ b/docs/CTXIAOMIPUSH.md
@@ -2,6 +2,11 @@
+## ⚠️ Deprecation Notice
+> Xiaomi Corporation made a significant announcement recently, notifying users about discontinuing the Mi Push service beyond Mainland China due to operational concerns. You might have already received communication regarding this matter.
+Read the official announcement from the Xiaomi Corporation [here](https://dev.mi.com/distribute/doc/details?pId=1555).
+With the Mi Push service's closure, CleverTap will cease offering Mi Push support for Xiaomi devices. After the shutdown, Xiaomi devices will still receive push notifications through Firebase Cloud Messaging (FCM).
+
## 👋 Introduction
[(Back to top)](#-table-of-contents)
diff --git a/docs/CTXIAOMIPUSHCHANGELOG.md b/docs/CTXIAOMIPUSHCHANGELOG.md
index fe529acf9..9d8d9e557 100644
--- a/docs/CTXIAOMIPUSHCHANGELOG.md
+++ b/docs/CTXIAOMIPUSHCHANGELOG.md
@@ -1,3 +1,8 @@
+## ⚠️ Deprecation Notice
+> Xiaomi Corporation made a significant announcement recently, notifying users about discontinuing the Mi Push service beyond Mainland China due to operational concerns. You might have already received communication regarding this matter.
+Read the official announcement from the Xiaomi Corporation [here](https://dev.mi.com/distribute/doc/details?pId=1555).
+With the Mi Push service's closure, CleverTap will cease offering Mi Push support for Xiaomi devices. After the shutdown, Xiaomi devices will still receive push notifications through Firebase Cloud Messaging (FCM).
+
## CleverTap Xiaomi Push SDK CHANGE LOG
### Version 1.5.4 (October 12, 2023)
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 38b9f31bb..f52f02e42 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -31,3 +31,9 @@
* First ensure that your CleverTap push notifications integration is working properly as described in [this guide](https://developer.clevertap.com/docs/android#section-push-notifications).
* For Android 6.0 or higher due to [Doze-Standby](https://developer.android.com/training/monitoring-device-state/doze-standby) and For Android 9.0 or higher due to [App standby buckets](https://developer.android.com/topic/performance/appstandby) network connectivity for apps gets deferred by some time as described [here in Network Column](https://developer.android.com/topic/performance/power/power-details) which prevents SDK to connect to CleverTap servers for raising notifications.
+
+7. When minifying is enabled for gradle wrapper 8.0+ and android gradle plugin 8.0.0+, R8 reports missing classes as errors (previously those were warnings) and the build fails.
+
+ * Upgrade to `com.clevertap.android:clevertap-android-sdk` v6.1.0 and `com.clevertap.android:clevertap-hms-sdk` v1.3.4 to fix this issue.
+ * This occurs due to change in behaviour in the AGP`
+ When R8 traces the program it will try to handle all the classes, methods and fields that it finds in the part of the program it considers live. Earlier during this tracing, it threw a warning which allowed building the apk. But these are now converted into errors. Details [here](https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 72401cbbc..e63565ae0 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -53,12 +53,12 @@ coroutines_test = "1.7.3"
installreferrer = "2.2"
#SDK Versions
-clevertap_android_sdk = "6.0.0"
+clevertap_android_sdk = "6.1.0"
clevertap_rendermax_sdk = "1.0.3"
-clevertap_geofence_sdk = "1.2.0"
-clevertap_hms_sdk = "1.3.3"
+clevertap_geofence_sdk = "1.3.0"
+clevertap_hms_sdk = "1.3.4"
clevertap_xiaomi_sdk = "1.5.4"
-clevertap_push_templates_sdk = "1.2.2"
+clevertap_push_templates_sdk = "1.2.3"
# Glide
glide = "4.12.0"
diff --git a/templates/CTCORECHANGELOG.md b/templates/CTCORECHANGELOG.md
index 576660b8e..6f1c9419b 100644
--- a/templates/CTCORECHANGELOG.md
+++ b/templates/CTCORECHANGELOG.md
@@ -1,5 +1,29 @@
## CleverTap Android SDK CHANGE LOG
+### Version 6.1.0 (February 21, 2024)
+
+#### New Features
+
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+* Deprecates Xiaomi public methods as we are sunsetting SDK. Details [here](https://dev.mi.com/distribute/doc/details?pId=1555).
+* Adds Accessibility ids for UI components of SDK
+* Migrates JobScheduler to WorkManager for Push Amplification.
+
+#### Breaking API Changes
+
+* **CTPushAmpWorker breaks custom WorkerFactory implementation of an App**:
+ * If you are using custom `WorkFactory` implementation of `WorkManager` then make sure that you
+ correctly handle workers defined by CleverTap SDK and other third party dependencies.
+ * You must return `null` from `createWorker()` for any unknown workerClassName. Please check
+ implementation provided in the
+ blog [here](https://medium.com/androiddevelopers/customizing-workmanager-fundamentals-fdaa17c46dd2)
+
+#### Bug Fixes
+
+* Fixes InApps crash in a rare activity destroyed race condition
+* Fixes Potential ANR in a race condition of SDK initialisation in multithreaded setup
+
### Version 6.0.0 (January 15, 2024)
#### New Features
@@ -104,7 +128,7 @@ Please remove the integrated Rendermax SDK before you upgrade to Android SDK v5.
correctly handle workers defined by CleverTap SDK and other third party dependencies.
* You must return `null` from `createWorker()` for any unknown workerClassName. Please check
implementation provided in the
- bolg [here](https://medium.com/androiddevelopers/customizing-workmanager-fundamentals-fdaa17c46dd2)
+ blog [here](https://medium.com/androiddevelopers/customizing-workmanager-fundamentals-fdaa17c46dd2)
* **Behavioral change of `createNotification` methods**:
* The following APIs now run on the caller's thread. Make sure to call it
diff --git a/templates/CTGEOFENCECHANGELOG.md b/templates/CTGEOFENCECHANGELOG.md
index 9f323230b..7574d31f5 100644
--- a/templates/CTGEOFENCECHANGELOG.md
+++ b/templates/CTGEOFENCECHANGELOG.md
@@ -1,5 +1,9 @@
## CleverTap Geofence SDK CHANGE LOG
+### Version 1.3.0 (February 21, 2024)
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+
### Version 1.2.0 (November 1, 2022)
* Updates [play-services-location](https://developers.google.com/android/guides/releases#october_13_2022) to `v21.0.0`
* Updates [work-runtime](https://developer.android.com/jetpack/androidx/releases/work#2.7.1) to `v2.7.1`
diff --git a/templates/CTHUAWEIPUSHCHANGELOG.md b/templates/CTHUAWEIPUSHCHANGELOG.md
index 561e14626..ef80cd049 100644
--- a/templates/CTHUAWEIPUSHCHANGELOG.md
+++ b/templates/CTHUAWEIPUSHCHANGELOG.md
@@ -1,5 +1,9 @@
## CleverTap Huawei Push SDK CHANGE LOG
+### Version 1.3.4 (February 21, 2024)
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+
### Version 1.3.3 (August 10, 2023)
* Updated Huawei Push SDK to v6.11.0.300
* Supports CleverTap Android SDK v5.2.0
diff --git a/templates/CTPUSHTEMPLATESCHANGELOG.md b/templates/CTPUSHTEMPLATESCHANGELOG.md
index 36232615d..9b806cfd6 100644
--- a/templates/CTPUSHTEMPLATESCHANGELOG.md
+++ b/templates/CTPUSHTEMPLATESCHANGELOG.md
@@ -1,5 +1,15 @@
## CleverTap Push Templates SDK CHANGE LOG
+### Version 1.2.3 (February 21, 2024)
+
+#### New features
+
+* Supports Android 14, made it compliant with Android 14 requirements. Details [here](https://developer.android.com/about/versions/14/summary)
+* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
+
+#### Bug Fixes
+* Fixes [Input Box](https://developer.clevertap.com/docs/push-templates-android#input-box-template) push template.
+
### Version 1.2.2 (January 15, 2024)
* Minor changes and improvements
diff --git a/templates/CTXIAOMIPUSH.md b/templates/CTXIAOMIPUSH.md
index 686d2683c..eb6716d57 100644
--- a/templates/CTXIAOMIPUSH.md
+++ b/templates/CTXIAOMIPUSH.md
@@ -2,6 +2,11 @@
+## ⚠️ Deprecation Notice
+> Xiaomi Corporation made a significant announcement recently, notifying users about discontinuing the Mi Push service beyond Mainland China due to operational concerns. You might have already received communication regarding this matter.
+Read the official announcement from the Xiaomi Corporation [here](https://dev.mi.com/distribute/doc/details?pId=1555).
+With the Mi Push service's closure, CleverTap will cease offering Mi Push support for Xiaomi devices. After the shutdown, Xiaomi devices will still receive push notifications through Firebase Cloud Messaging (FCM).
+
## 👋 Introduction
[(Back to top)](#-table-of-contents)
diff --git a/templates/CTXIAOMIPUSHCHANGELOG.md b/templates/CTXIAOMIPUSHCHANGELOG.md
index fe529acf9..9d8d9e557 100644
--- a/templates/CTXIAOMIPUSHCHANGELOG.md
+++ b/templates/CTXIAOMIPUSHCHANGELOG.md
@@ -1,3 +1,8 @@
+## ⚠️ Deprecation Notice
+> Xiaomi Corporation made a significant announcement recently, notifying users about discontinuing the Mi Push service beyond Mainland China due to operational concerns. You might have already received communication regarding this matter.
+Read the official announcement from the Xiaomi Corporation [here](https://dev.mi.com/distribute/doc/details?pId=1555).
+With the Mi Push service's closure, CleverTap will cease offering Mi Push support for Xiaomi devices. After the shutdown, Xiaomi devices will still receive push notifications through Firebase Cloud Messaging (FCM).
+
## CleverTap Xiaomi Push SDK CHANGE LOG
### Version 1.5.4 (October 12, 2023)
diff --git a/templates/FAQ.md b/templates/FAQ.md
index 38b9f31bb..f52f02e42 100644
--- a/templates/FAQ.md
+++ b/templates/FAQ.md
@@ -31,3 +31,9 @@
* First ensure that your CleverTap push notifications integration is working properly as described in [this guide](https://developer.clevertap.com/docs/android#section-push-notifications).
* For Android 6.0 or higher due to [Doze-Standby](https://developer.android.com/training/monitoring-device-state/doze-standby) and For Android 9.0 or higher due to [App standby buckets](https://developer.android.com/topic/performance/appstandby) network connectivity for apps gets deferred by some time as described [here in Network Column](https://developer.android.com/topic/performance/power/power-details) which prevents SDK to connect to CleverTap servers for raising notifications.
+
+7. When minifying is enabled for gradle wrapper 8.0+ and android gradle plugin 8.0.0+, R8 reports missing classes as errors (previously those were warnings) and the build fails.
+
+ * Upgrade to `com.clevertap.android:clevertap-android-sdk` v6.1.0 and `com.clevertap.android:clevertap-hms-sdk` v1.3.4 to fix this issue.
+ * This occurs due to change in behaviour in the AGP`
+ When R8 traces the program it will try to handle all the classes, methods and fields that it finds in the part of the program it considers live. Earlier during this tracing, it threw a warning which allowed building the apk. But these are now converted into errors. Details [here](https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes)
From 13f8c0641079390a260eb878d95b8215d621f893 Mon Sep 17 00:00:00 2001
From: Anush-Shand <127097095+Anush-Shand@users.noreply.github.com>
Date: Wed, 21 Feb 2024 18:50:16 +0530
Subject: [PATCH 27/29] Docs/release cadence feb21 fix (#562)
* Changelog: Xiaomi deprecations
- deprecations added in xiaomi docs
* Changelog: Xiaomi deprecations contd...
- deprecations added in xiaomi docs
* Changelog: Core SDK changelog
* Changelog: Core SDK changelog
* Changelog: SDK changelogs
- push templates, geofence and huawei change logs updated.
* Changelog: Core changelog
* Changelog: FAQs updated
* Changelog: Main changelog
- hyperlinks individual ones
* Versioning: SDK versions
- bumped for new release
- did for all but xiaomi sdks.
* Chore: Copy templates
- ran copy templates gradle command.
* docs(SDK-3672) - Minor updates to docs
* docs - Update changelog to include github issue fix
---------
Co-authored-by: CTLalit
---
docs/CTCORECHANGELOG.md | 1 +
templates/CTCORECHANGELOG.md | 1 +
2 files changed, 2 insertions(+)
diff --git a/docs/CTCORECHANGELOG.md b/docs/CTCORECHANGELOG.md
index 6f1c9419b..290ccaf8c 100644
--- a/docs/CTCORECHANGELOG.md
+++ b/docs/CTCORECHANGELOG.md
@@ -23,6 +23,7 @@
* Fixes InApps crash in a rare activity destroyed race condition
* Fixes Potential ANR in a race condition of SDK initialisation in multithreaded setup
+* Fixes [#456](https://github.com/CleverTap/clevertap-android-sdk/issues/428) - Build issues due to AGP 8
### Version 6.0.0 (January 15, 2024)
diff --git a/templates/CTCORECHANGELOG.md b/templates/CTCORECHANGELOG.md
index 6f1c9419b..290ccaf8c 100644
--- a/templates/CTCORECHANGELOG.md
+++ b/templates/CTCORECHANGELOG.md
@@ -23,6 +23,7 @@
* Fixes InApps crash in a rare activity destroyed race condition
* Fixes Potential ANR in a race condition of SDK initialisation in multithreaded setup
+* Fixes [#456](https://github.com/CleverTap/clevertap-android-sdk/issues/428) - Build issues due to AGP 8
### Version 6.0.0 (January 15, 2024)
From ef053825084332682702b8c70a1264be46dad32c Mon Sep 17 00:00:00 2001
From: CTLalit <144685420+CTLalit@users.noreply.github.com>
Date: Mon, 26 Feb 2024 12:57:18 +0530
Subject: [PATCH 28/29] docs(SDK-3672) - Replaces Push Amplification with Pull
Notifications in docs and comments (#567)
Co-authored-by: anush
---
CHANGELOG.md | 6 +++---
.../main/java/com/clevertap/android/sdk/CleverTapAPI.java | 4 ++--
.../clevertap/android/sdk/interfaces/IPushAmpHandler.java | 4 ++--
.../android/sdk/pushnotification/PushProviders.java | 4 ++--
.../sdk/pushnotification/fcm/CTFcmMessageHandler.java | 2 +-
.../com/clevertap/android/sdk/response/PushAmpResponse.java | 2 +-
.../java/com/clevertap/android/hms/CTHmsMessageHandler.java | 2 +-
.../com/clevertap/android/xps/CTXiaomiMessageHandler.java | 2 +-
docs/CTCORECHANGELOG.md | 4 ++--
docs/CTHUAWEIPUSHCHANGELOG.md | 2 +-
docs/CTPUSHTEMPLATESCHANGELOG.md | 2 +-
docs/CTV4CHANGES.md | 6 +++---
docs/CTXIAOMIPUSHCHANGELOG.md | 2 +-
docs/EXAMPLES.md | 6 +++---
docs/FAQ.md | 4 ++--
templates/CTCORECHANGELOG.md | 4 ++--
templates/CTHUAWEIPUSHCHANGELOG.md | 2 +-
templates/CTPUSHTEMPLATESCHANGELOG.md | 2 +-
templates/CTV4CHANGES.md | 6 +++---
templates/CTXIAOMIPUSHCHANGELOG.md | 2 +-
templates/EXAMPLES.md | 6 +++---
templates/FAQ.md | 4 ++--
22 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f73b922ad..90361ef84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -223,7 +223,7 @@
* Use v3.8.2
* Adds support for Product Config and Feature Flag as a part of Product Experiences feature
* Fixed InApp center alignment issue for tablets
-* Adds support for custom handling payload when using Push Amplification.
+* Adds support for custom handling payload when using Pull Notifications.
* Other bug fixes
### Version 3.7.2 (March 27, 2020)
@@ -264,7 +264,7 @@
* Adds support for deep link query parameters in InApps.
* Deprecated GCM.
* Deprecated EventHandler, SessionHandler and DataHandler classes.
-* Workaround for below Oreo Android OS bug causing ANRs while using Push Amplification.
+* Workaround for below Oreo Android OS bug causing ANRs while using Pull Notifications.
* Bug fixes and performance improvements
### Version 3.5.1 (May 24, 2019)
@@ -294,7 +294,7 @@
### Version 3.4.0 (January 14, 2019)
* Adds support for App Inbox
-* Adds support for Push Amplification
+* Adds support for Pull Notifications
* Workaround for Android O orientation bug in Native InApps
* Fixes a bug which led to ANR on 2G network
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
index 7ee4572d8..c2b6522f6 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/CleverTapAPI.java
@@ -272,10 +272,10 @@ CleverTapAPI getGlobalInstance(Context context, String _accountId) {
}
/**
- * Pass Push Notification Payload to CleverTap for smooth functioning of Push Amplification
+ * Pass Push Notification Payload to CleverTap for smooth functioning of Pull Notifications
*
* @param context - Application Context
- * @param extras - Bundle received via FCM/Push Amplification
+ * @param extras - Bundle received via FCM/Pull Notifications
*/
@SuppressWarnings("unused")
public static void processPushNotification(Context context, Bundle extras) {
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/interfaces/IPushAmpHandler.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/interfaces/IPushAmpHandler.java
index 5f133c523..943e7c94d 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/interfaces/IPushAmpHandler.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/interfaces/IPushAmpHandler.java
@@ -4,7 +4,7 @@
import androidx.annotation.NonNull;
/**
- * Generic Interface to handle push amplification for different types of notification messages, received from
+ * Generic Interface to handle Pull Notifications for different types of notification messages, received from
* respective services or receivers(ex. FirebaseMessagingService).
*
* Implement this interface if you want to support push amp for different types of notification messages.
@@ -13,7 +13,7 @@
public interface IPushAmpHandler {
/**
- * Processes notification message for push amplification
+ * Processes notification message for Pull Notifications
* @param context application context
* @param message notification message received from cloud messaging provider like firebase,xiaomi,huawei etc.
*/
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java
index 924ea4f34..66e0317b3 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/PushProviders.java
@@ -365,9 +365,9 @@ public void onTokenRefresh() {
}
/**
- * Stores silent push notification in DB for smooth working of Push Amplification
+ * Stores silent push notification in DB for smooth working of Pull Notifications
* Background Job Service and also stores wzrk_pid to the DB to avoid duplication of Push
- * Notifications from Push Amplification.
+ * Notifications from Pull Notifications.
*
* @param extras - Bundle
*/
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/fcm/CTFcmMessageHandler.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/fcm/CTFcmMessageHandler.java
index f64a336e1..7dcb3037e 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/fcm/CTFcmMessageHandler.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/pushnotification/fcm/CTFcmMessageHandler.java
@@ -85,7 +85,7 @@ public boolean onNewToken(final Context applicationContext, final String token)
* {@inheritDoc}
*
* Use this method if you are rendering notification by your own and wants to support your custom rendered
- * notification for push amplification
+ * notification for Pull Notifications
*/
@Override
public void processPushAmp(final Context context, @NonNull final RemoteMessage message) {
diff --git a/clevertap-core/src/main/java/com/clevertap/android/sdk/response/PushAmpResponse.java b/clevertap-core/src/main/java/com/clevertap/android/sdk/response/PushAmpResponse.java
index d4d354ba1..31a3066f0 100644
--- a/clevertap-core/src/main/java/com/clevertap/android/sdk/response/PushAmpResponse.java
+++ b/clevertap-core/src/main/java/com/clevertap/android/sdk/response/PushAmpResponse.java
@@ -47,7 +47,7 @@ public PushAmpResponse(
@Override
public void processResponse(final JSONObject response, final String stringBody, final Context context) {
- //Handle Push Amplification response
+ //Handle Pull Notifications response
if (config.isAnalyticsOnly()) {
logger.verbose(config.getAccountId(),
"CleverTap instance is configured to analytics only, not processing push amp response");
diff --git a/clevertap-hms/src/main/java/com/clevertap/android/hms/CTHmsMessageHandler.java b/clevertap-hms/src/main/java/com/clevertap/android/hms/CTHmsMessageHandler.java
index c135a72d8..a613a178f 100644
--- a/clevertap-hms/src/main/java/com/clevertap/android/hms/CTHmsMessageHandler.java
+++ b/clevertap-hms/src/main/java/com/clevertap/android/hms/CTHmsMessageHandler.java
@@ -81,7 +81,7 @@ public boolean onNewToken(Context context, final String token) {
* {@inheritDoc}
*
* Use this method if you are rendering notification by your own and wants to support your custom rendered
- * notification for push amplification
+ * notification for Pull Notifications
*/
@Override
public void processPushAmp(final Context context, @NonNull final RemoteMessage message) {
diff --git a/clevertap-xps/src/main/java/com/clevertap/android/xps/CTXiaomiMessageHandler.java b/clevertap-xps/src/main/java/com/clevertap/android/xps/CTXiaomiMessageHandler.java
index 6852dad14..5fc503ce6 100644
--- a/clevertap-xps/src/main/java/com/clevertap/android/xps/CTXiaomiMessageHandler.java
+++ b/clevertap-xps/src/main/java/com/clevertap/android/xps/CTXiaomiMessageHandler.java
@@ -116,7 +116,7 @@ int onReceiveRegisterResult(Context context, MiPushCommandMessage miPushCommandM
* {@inheritDoc}
*
* Use this method if you are rendering notification by your own and wants to support your custom rendered
- * notification for push amplification
+ * notification for Pull Notifications
*/
@Override
public void processPushAmp(final Context context, @NonNull final MiPushMessage message) {
diff --git a/docs/CTCORECHANGELOG.md b/docs/CTCORECHANGELOG.md
index 290ccaf8c..c9fdaa909 100644
--- a/docs/CTCORECHANGELOG.md
+++ b/docs/CTCORECHANGELOG.md
@@ -8,7 +8,7 @@
* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
* Deprecates Xiaomi public methods as we are sunsetting SDK. Details [here](https://dev.mi.com/distribute/doc/details?pId=1555).
* Adds Accessibility ids for UI components of SDK
-* Migrates JobScheduler to WorkManager for Push Amplification.
+* Migrates JobScheduler to WorkManager for Pull Notifications.
#### Breaking API Changes
@@ -299,7 +299,7 @@ Please remove the integrated Rendermax SDK before you upgrade to Android SDK v5.
Note : If you are facing `ClassNotFoundException` "org.jacoco.agent.rt.internal_28bab1d.Offline" after updating to 4.5.0, Please update the SDK to v4.5.1
### Version 4.4.0 (December 20, 2021)
-* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(FCM),Custom Push Amplification Handling and Push Templates
+* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(FCM),Custom Pull Notifications Handling and Push Templates
* `CTFcmMessageHandler().createNotification(applicationContext, message)`
* `CTFcmMessageHandler().processPushAmp(applicationContext, message)`
* `CleverTapAPI.setNotificationHandler(notificationHandler)`
diff --git a/docs/CTHUAWEIPUSHCHANGELOG.md b/docs/CTHUAWEIPUSHCHANGELOG.md
index ef80cd049..a9488f878 100644
--- a/docs/CTHUAWEIPUSHCHANGELOG.md
+++ b/docs/CTHUAWEIPUSHCHANGELOG.md
@@ -20,7 +20,7 @@
* Updated Huawei Push SDK to v6.3.0.304
### Version 1.2.0 (December 20, 2021)
-* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(HMS),Custom Push Amplification Handling and Push Templates.
+* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(HMS),Custom Pull Notifications Handling and Push Templates.
* `CTHmsMessageHandler().createNotification(applicationContext,message)`
* `CTHmsMessageHandler().processPushAmp(applicationContext,message)`
* Supports CleverTap Android SDK v4.4.0
diff --git a/docs/CTPUSHTEMPLATESCHANGELOG.md b/docs/CTPUSHTEMPLATESCHANGELOG.md
index 9b806cfd6..60849049c 100644
--- a/docs/CTPUSHTEMPLATESCHANGELOG.md
+++ b/docs/CTPUSHTEMPLATESCHANGELOG.md
@@ -93,7 +93,7 @@
### Version 1.0.0 (December 20, 2021)
* Stable release! 🎉
* Supports Xiaomi, Huawei notification messages out of the box
-* Supports Push Amplification out of the box
+* Supports Pull Notifications out of the box
* Supports Android 12
* Supports CleverTap Android SDK v4.4.0
diff --git a/docs/CTV4CHANGES.md b/docs/CTV4CHANGES.md
index bacba9461..52297c67d 100644
--- a/docs/CTV4CHANGES.md
+++ b/docs/CTV4CHANGES.md
@@ -11,7 +11,7 @@
* [Breaking changes](#%EF%B8%8F-breaking-changes)
* [Firebase Messaging changes](#-firebase-messaging-changes)
* [Push Notification changes](#-push-notification-changes)
- * [Push Amplification changes](#-push-amplification-changes)
+ * [Pull Notifications changes](#-push-amplification-changes)
* [Questions](#-questions)
## ♻️ Migration
@@ -149,7 +149,7 @@ New `AndroidManifest.xml` entries
`com.clevertap.android.sdk.NotificationInfo` has been renamed to `com.clevertap.android.sdk.pushnotification.NotificationInfo`
-### 📲 Push Amplification changes
+### 📲 Pull Notifications changes
The following `AndroidManifest.xml` entries are no longer needed to be added -
@@ -170,7 +170,7 @@ The following `AndroidManifest.xml` entries are no longer needed to be added -
android:exported="false"/>
```
-To enable Push Amplification only the following entry is required in the `AndroidManifest.xml` file -
+To enable Pull Notifications only the following entry is required in the `AndroidManifest.xml` file -
```xml
* For Android 6.0 or higher due to [Doze-Standby](https://developer.android.com/training/monitoring-device-state/doze-standby) and For Android 9.0 or higher due to [App standby buckets](https://developer.android.com/topic/performance/appstandby) network connectivity for apps gets deferred by some time as described [here in Network Column](https://developer.android.com/topic/performance/power/power-details) which prevents SDK to connect to CleverTap servers for raising notifications.
-7. When minifying is enabled for gradle wrapper 8.0+ and android gradle plugin 8.0.0+, R8 reports missing classes as errors (previously those were warnings) and the build fails.
+7. Why does the build fail for an app when minifying is enabled for gradle wrapper 8.0+ and android gradle plugin 8.0.0+?
- * Upgrade to `com.clevertap.android:clevertap-android-sdk` v6.1.0 and `com.clevertap.android:clevertap-hms-sdk` v1.3.4 to fix this issue.
* This occurs due to change in behaviour in the AGP`
When R8 traces the program it will try to handle all the classes, methods and fields that it finds in the part of the program it considers live. Earlier during this tracing, it threw a warning which allowed building the apk. But these are now converted into errors. Details [here](https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes)
+ * Upgrade to `com.clevertap.android:clevertap-android-sdk` v6.1.0 and `com.clevertap.android:clevertap-hms-sdk` v1.3.4 to fix this issue.
\ No newline at end of file
diff --git a/templates/CTCORECHANGELOG.md b/templates/CTCORECHANGELOG.md
index 290ccaf8c..c9fdaa909 100644
--- a/templates/CTCORECHANGELOG.md
+++ b/templates/CTCORECHANGELOG.md
@@ -8,7 +8,7 @@
* Upgrades AGP to 8.2.2 for building the SDK and adds related consumer proguard rules
* Deprecates Xiaomi public methods as we are sunsetting SDK. Details [here](https://dev.mi.com/distribute/doc/details?pId=1555).
* Adds Accessibility ids for UI components of SDK
-* Migrates JobScheduler to WorkManager for Push Amplification.
+* Migrates JobScheduler to WorkManager for Pull Notifications.
#### Breaking API Changes
@@ -299,7 +299,7 @@ Please remove the integrated Rendermax SDK before you upgrade to Android SDK v5.
Note : If you are facing `ClassNotFoundException` "org.jacoco.agent.rt.internal_28bab1d.Offline" after updating to 4.5.0, Please update the SDK to v4.5.1
### Version 4.4.0 (December 20, 2021)
-* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(FCM),Custom Push Amplification Handling and Push Templates
+* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(FCM),Custom Pull Notifications Handling and Push Templates
* `CTFcmMessageHandler().createNotification(applicationContext, message)`
* `CTFcmMessageHandler().processPushAmp(applicationContext, message)`
* `CleverTapAPI.setNotificationHandler(notificationHandler)`
diff --git a/templates/CTHUAWEIPUSHCHANGELOG.md b/templates/CTHUAWEIPUSHCHANGELOG.md
index ef80cd049..a9488f878 100644
--- a/templates/CTHUAWEIPUSHCHANGELOG.md
+++ b/templates/CTHUAWEIPUSHCHANGELOG.md
@@ -20,7 +20,7 @@
* Updated Huawei Push SDK to v6.3.0.304
### Version 1.2.0 (December 20, 2021)
-* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(HMS),Custom Push Amplification Handling and Push Templates.
+* Adds below new public APIs for smooth and easy integration of Custom Android Push Notifications Handling(HMS),Custom Pull Notifications Handling and Push Templates.
* `CTHmsMessageHandler().createNotification(applicationContext,message)`
* `CTHmsMessageHandler().processPushAmp(applicationContext,message)`
* Supports CleverTap Android SDK v4.4.0
diff --git a/templates/CTPUSHTEMPLATESCHANGELOG.md b/templates/CTPUSHTEMPLATESCHANGELOG.md
index 9b806cfd6..60849049c 100644
--- a/templates/CTPUSHTEMPLATESCHANGELOG.md
+++ b/templates/CTPUSHTEMPLATESCHANGELOG.md
@@ -93,7 +93,7 @@
### Version 1.0.0 (December 20, 2021)
* Stable release! 🎉
* Supports Xiaomi, Huawei notification messages out of the box
-* Supports Push Amplification out of the box
+* Supports Pull Notifications out of the box
* Supports Android 12
* Supports CleverTap Android SDK v4.4.0
diff --git a/templates/CTV4CHANGES.md b/templates/CTV4CHANGES.md
index bacba9461..52297c67d 100644
--- a/templates/CTV4CHANGES.md
+++ b/templates/CTV4CHANGES.md
@@ -11,7 +11,7 @@
* [Breaking changes](#%EF%B8%8F-breaking-changes)
* [Firebase Messaging changes](#-firebase-messaging-changes)
* [Push Notification changes](#-push-notification-changes)
- * [Push Amplification changes](#-push-amplification-changes)
+ * [Pull Notifications changes](#-push-amplification-changes)
* [Questions](#-questions)
## ♻️ Migration
@@ -149,7 +149,7 @@ New `AndroidManifest.xml` entries
`com.clevertap.android.sdk.NotificationInfo` has been renamed to `com.clevertap.android.sdk.pushnotification.NotificationInfo`
-### 📲 Push Amplification changes
+### 📲 Pull Notifications changes
The following `AndroidManifest.xml` entries are no longer needed to be added -
@@ -170,7 +170,7 @@ The following `AndroidManifest.xml` entries are no longer needed to be added -
android:exported="false"/>
```
-To enable Push Amplification only the following entry is required in the `AndroidManifest.xml` file -
+To enable Pull Notifications only the following entry is required in the `AndroidManifest.xml` file -
```xml
* For Android 6.0 or higher due to [Doze-Standby](https://developer.android.com/training/monitoring-device-state/doze-standby) and For Android 9.0 or higher due to [App standby buckets](https://developer.android.com/topic/performance/appstandby) network connectivity for apps gets deferred by some time as described [here in Network Column](https://developer.android.com/topic/performance/power/power-details) which prevents SDK to connect to CleverTap servers for raising notifications.
-7. When minifying is enabled for gradle wrapper 8.0+ and android gradle plugin 8.0.0+, R8 reports missing classes as errors (previously those were warnings) and the build fails.
+7. Why does the build fail for an app when minifying is enabled for gradle wrapper 8.0+ and android gradle plugin 8.0.0+?
- * Upgrade to `com.clevertap.android:clevertap-android-sdk` v6.1.0 and `com.clevertap.android:clevertap-hms-sdk` v1.3.4 to fix this issue.
* This occurs due to change in behaviour in the AGP`
When R8 traces the program it will try to handle all the classes, methods and fields that it finds in the part of the program it considers live. Earlier during this tracing, it threw a warning which allowed building the apk. But these are now converted into errors. Details [here](https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes)
+ * Upgrade to `com.clevertap.android:clevertap-android-sdk` v6.1.0 and `com.clevertap.android:clevertap-hms-sdk` v1.3.4 to fix this issue.
\ No newline at end of file
From bc2d99c87332563be1eaf075bee0a9d82913a3d8 Mon Sep 17 00:00:00 2001
From: Anush-Shand <127097095+Anush-Shand@users.noreply.github.com>
Date: Mon, 26 Feb 2024 13:25:32 +0530
Subject: [PATCH 29/29] task(SDK-3685) - Adds variables, CS InApps to sample
app (#566)
Co-authored-by: CTLalit <144685420+CTLalit@users.noreply.github.com>
---
sample/build.gradle | 24 +++----
sample/proguard-rules.pro | 3 +-
.../com/clevertap/demo/ExampleVariables.java | 56 +++++++++++++++++
.../clevertap/demo/ui/main/HomeScreenModel.kt | 16 ++++-
.../demo/ui/main/HomeScreenViewModel.kt | 62 +++++++++++++++++++
5 files changed, 147 insertions(+), 14 deletions(-)
create mode 100644 sample/src/main/java/com/clevertap/demo/ExampleVariables.java
diff --git a/sample/build.gradle b/sample/build.gradle
index 8f9026b98..44bee891e 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -92,7 +92,7 @@ dependencies {
implementation("com.github.khirr:Android-Privacy-Policy:1.0.3")
localImplementation(project(":clevertap-core")) //CleverTap Android SDK, make sure the AAR file is in the libs folder
- //implementation fileTree(include: ['*.aar'], dir: 'libs')
+ implementation fileTree(include: ['*.aar'], dir: 'libs')
localImplementation(project(":clevertap-geofence")) // Geofence
localImplementation(project(":clevertap-xps")) // For Xiaomi Push use
localImplementation(project(":clevertap-pushtemplates"))
@@ -136,17 +136,17 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
implementation "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1"*/
- remoteImplementation("com.clevertap.android:clevertap-android-sdk:5.2.1")
- remoteImplementation("com.clevertap.android:clevertap-geofence-sdk:1.2.0")
- remoteImplementation("com.clevertap.android:clevertap-xiaomi-sdk:1.5.2")
- remoteImplementation("com.clevertap.android:push-templates:1.1.0")
- remoteImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.2")
-
- stagingImplementation("com.clevertap.android:clevertap-android-sdk:5.2.1")
- stagingImplementation("com.clevertap.android:clevertap-geofence-sdk:1.2.0")
- stagingImplementation("com.clevertap.android:clevertap-xiaomi-sdk:1.5.2")
- stagingImplementation("com.clevertap.android:push-templates:1.1.0")
- stagingImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.2")
+ remoteImplementation("com.clevertap.android:clevertap-android-sdk:6.1.0")
+ remoteImplementation("com.clevertap.android:clevertap-geofence-sdk:1.3.0")
+ remoteImplementation("com.clevertap.android:clevertap-xiaomi-sdk:1.5.4")
+ remoteImplementation("com.clevertap.android:push-templates:1.2.3")
+ remoteImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.4")
+
+ stagingImplementation("com.clevertap.android:clevertap-android-sdk:6.1.0")
+ stagingImplementation("com.clevertap.android:clevertap-geofence-sdk:1.3.0")
+ stagingImplementation("com.clevertap.android:clevertap-xiaomi-sdk:1.5.4")
+ stagingImplementation("com.clevertap.android:push-templates:1.2.3")
+ stagingImplementation("com.clevertap.android:clevertap-hms-sdk:1.3.4")
}
apply plugin: 'com.google.gms.google-services'
diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro
index dd9cc0560..cce288252 100644
--- a/sample/proguard-rules.pro
+++ b/sample/proguard-rules.pro
@@ -30,4 +30,5 @@
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
--keep class com.huawei.hms.**{*;}
\ No newline at end of file
+-keep class com.huawei.hms.**{*;}
+-keep class com.clevertap.demo.ExampleVariables {*;}
\ No newline at end of file
diff --git a/sample/src/main/java/com/clevertap/demo/ExampleVariables.java b/sample/src/main/java/com/clevertap/demo/ExampleVariables.java
new file mode 100644
index 000000000..12ed3f9b6
--- /dev/null
+++ b/sample/src/main/java/com/clevertap/demo/ExampleVariables.java
@@ -0,0 +1,56 @@
+package com.clevertap.demo;
+
+import com.clevertap.android.sdk.variables.annotations.Variable;
+import com.clevertap.android.sdk.variables.callbacks.VariablesChangedCallback;
+
+public class ExampleVariables {
+
+ @Variable
+ public boolean var_boolean = true;
+
+ @Variable
+ public byte var_byte = 1;
+
+ @Variable
+ public short var_short = 2;
+
+ @Variable
+ public int var_int = 3;
+
+ @Variable
+ public long var_long = 4L;
+
+ @Variable
+ public float var_float = 5F;
+
+ @Variable
+ public double var_double = 6.;
+
+ @Variable
+ public String var_string = "str";
+
+ private final VariablesChangedCallback oneTimeVariablesChangedCallback;
+ private final VariablesChangedCallback variablesChangedCallback;
+
+ public VariablesChangedCallback getOneTimeVariablesChangedCallback() {
+ return oneTimeVariablesChangedCallback;
+ }
+
+ public VariablesChangedCallback getVariablesChangedCallback() {
+ return variablesChangedCallback;
+ }
+
+ public ExampleVariables() {
+ oneTimeVariablesChangedCallback = new VariablesChangedCallback() {
+ @Override public void variablesChanged() {
+ System.out.println("One Time Variables Changed");
+ }
+ };
+
+ variablesChangedCallback = new VariablesChangedCallback() {
+ @Override public void variablesChanged() {
+ System.out.println("Variables Changed");
+ }
+ };
+ }
+}
diff --git a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenModel.kt b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenModel.kt
index 8e80bf47e..7937a8280 100644
--- a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenModel.kt
+++ b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenModel.kt
@@ -86,7 +86,21 @@ object HomeScreenModel {
"Hard permission dialog with fallbackToSettings - false",
"Hard permission dialog with fallbackToSettings - true"
),
- "INAPP" to listOf("Footer InApp", "Footer InApp without image", "Header")
+ "INAPP" to listOf("Footer InApp", "Footer InApp without image", "Header"),
+ "CS INAPP" to listOf("Fetch CS InApps", "Clear all CS InApp Resources", "Clear expired only InAPP Resources"),
+ "VARIABLES" to listOf(
+ "Define Variable",
+ "Fetch Variables",
+ "Sync Variables",
+ "Parse Variables",
+ "Get Variable",
+ "Get Variable Value",
+ "Add Variables Changed Callback",
+ "Remove Variables Changed Callback",
+ "Add One Time Variables Changed Callback",
+ "Remove One Time Variables Changed Callback"
+ ),
+ "LOCALE" to listOf("Set Locale")
)
}
}
\ No newline at end of file
diff --git a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenViewModel.kt b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenViewModel.kt
index 49bc14c99..94e35bc30 100644
--- a/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenViewModel.kt
+++ b/sample/src/main/java/com/clevertap/demo/ui/main/HomeScreenViewModel.kt
@@ -8,6 +8,8 @@ import com.clevertap.android.sdk.CTInboxStyleConfig
import com.clevertap.android.sdk.CleverTapAPI
import com.clevertap.android.sdk.Constants
import com.clevertap.android.sdk.inapp.CTLocalInApp
+import com.clevertap.android.sdk.inapp.callbacks.FetchInAppsCallback
+import com.clevertap.demo.ExampleVariables
import java.util.Date
class HomeScreenViewModel(private val cleverTapAPI: CleverTapAPI?) : ViewModel() {
@@ -20,6 +22,8 @@ class HomeScreenViewModel(private val cleverTapAPI: CleverTapAPI?) : ViewModel()
Log.i("HomeScreenViewModel", "child click $groupPosition $childPosition")
val commandPosition = "$groupPosition-$childPosition"
clickCommand.value = commandPosition
+ val exampleVariables = ExampleVariables()
+
when (commandPosition) {
"0-0" -> {
cleverTapAPI?.pushEvent("testEventPushAmp")
@@ -492,6 +496,64 @@ class HomeScreenViewModel(private val cleverTapAPI: CleverTapAPI?) : ViewModel()
cleverTapAPI?.pushEvent("Header")
}
+ "12-0" -> {
+ cleverTapAPI?.fetchInApps( object : FetchInAppsCallback {
+ override fun onInAppsFetched(isSuccess: Boolean) {
+ println("InAppsFetched = $isSuccess")
+ }
+ })
+ }
+ "12-1" -> {
+ cleverTapAPI?.clearInAppResources(false)
+ }
+ "12-2" -> {
+ cleverTapAPI?.clearInAppResources(true)
+ }
+
+ "13-0" -> {
+ cleverTapAPI?.defineVariable("variableInt", 0)
+ cleverTapAPI?.defineVariable("variableBoolean", true)
+ cleverTapAPI?.defineVariable("variableFloat", 2.4f)
+ }
+ "13-1" -> {
+ cleverTapAPI?.fetchVariables { isSuccess -> println("Variables Fetched = $isSuccess") }
+ }
+ "13-2" -> {
+ cleverTapAPI?.syncVariables()
+ }
+ "13-3" -> {
+ cleverTapAPI?.parseVariables(exampleVariables)
+ }
+ "13-4" -> {
+ println("VariableInt = ${cleverTapAPI?.getVariable("variableInt")}")
+ println("VariableBoolean = ${cleverTapAPI?.getVariable("variableBoolean")}")
+ println("VariableFloat = ${cleverTapAPI?.getVariable("variableFloat")}")
+ println("ParsedVariableDouble = ${cleverTapAPI?.getVariable("var_double")}")
+ }
+ "13-5" -> {
+ println("VariableInt = ${cleverTapAPI?.getVariableValue("variableInt")}")
+ println("VariableBoolean = ${cleverTapAPI?.getVariableValue("variableBoolean")}")
+ println("VariableFloat = ${cleverTapAPI?.getVariableValue("variableFloat")}")
+ println("ParsedVariableDouble = ${cleverTapAPI?.getVariableValue("var_double")}")
+ }
+ "13-6" -> {
+
+ cleverTapAPI?.addVariablesChangedCallback(exampleVariables.variablesChangedCallback)
+ }
+ "13-7" -> {
+ cleverTapAPI?.removeVariablesChangedCallback(exampleVariables.variablesChangedCallback)
+
+ }
+ "13-8" -> {
+ cleverTapAPI?.addOneTimeVariablesChangedCallback(exampleVariables.oneTimeVariablesChangedCallback)
+ }
+ "13-9" -> {
+ cleverTapAPI?.removeOneTimeVariablesChangedCallback(exampleVariables.oneTimeVariablesChangedCallback)
+ }
+
+ "14-0" -> {
+ cleverTapAPI?.locale = "en_IN"
+ }
//"60" -> webViewClickListener?.onWebViewClick()
}