Skip to content

Commit 0f1d4b8

Browse files
authored
Merge branch 'master' into MOB-8451
2 parents ff03b0a + fa056fe commit 0f1d4b8

File tree

10 files changed

+181
-41
lines changed

10 files changed

+181
-41
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1313
#### Changed
1414
- nothing yet
1515

16+
## [3.5.2]
17+
#### Added
18+
- Introducing a new method `setAuthRetryPolicy` in `IterableConfig` to set the retry policy for JWT token refresh.
19+
- You can now use method - `pauseAuthRetries(boolean pauseRetry)` to pause or resume the JWT token refresh retries.
20+
- `trackPurchase` method now accepts `attributionInfo` parameter to track purchase with attribution information.
21+
22+
#### Fixed
23+
- Embedded manager listeners are active only if feature is enabled
24+
1625
## [3.5.1]
1726

1827
#### Fixed

iterableapi-ui/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ dependencies {
5151

5252
ext {
5353
libraryName = 'iterableapi-ui'
54-
libraryVersion = '3.5.1'
54+
libraryVersion = '3.5.2'
5555
}
5656

5757
if (hasProperty("mavenPublishEnabled")) {

iterableapi/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ android {
1919
minSdkVersion 16
2020
targetSdkVersion 27
2121

22-
buildConfigField "String", "ITERABLE_SDK_VERSION", "\"3.5.1\""
22+
buildConfigField "String", "ITERABLE_SDK_VERSION", "\"3.5.2\""
2323

2424
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
2525
}
@@ -85,7 +85,7 @@ dependencies {
8585

8686
ext {
8787
libraryName = 'iterableapi'
88-
libraryVersion = '3.5.1'
88+
libraryVersion = '3.5.2'
8989
}
9090

9191
if (hasProperty("mavenPublishEnabled")) {

iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ Context getMainActivityContext() {
131131
@NonNull
132132
IterableAuthManager getAuthManager() {
133133
if (authManager == null) {
134-
authManager = new IterableAuthManager(this, config.authHandler, config.expiringAuthTokenRefreshPeriod);
134+
authManager = new IterableAuthManager(this, config.authHandler, config.retryPolicy, config.expiringAuthTokenRefreshPeriod);
135135
}
136136
return authManager;
137137
}
@@ -344,7 +344,7 @@ private void logoutPreviousUser() {
344344

345345
getInAppManager().reset();
346346
getEmbeddedManager().reset();
347-
getAuthManager().clearRefreshTimer();
347+
getAuthManager().reset();
348348

349349
apiClient.onLogout();
350350
}
@@ -355,6 +355,7 @@ private void onLogin(@Nullable String authToken) {
355355
return;
356356
}
357357

358+
getAuthManager().pauseAuthRetries(false);
358359
if (authToken != null) {
359360
setAuthToken(authToken);
360361
} else {
@@ -457,7 +458,7 @@ private void retrieveEmailAndUserId() {
457458
getAuthManager().queueExpirationRefresh(_authToken);
458459
} else {
459460
IterableLogger.d(TAG, "Auth token found as null. Scheduling token refresh in 10 seconds...");
460-
getAuthManager().scheduleAuthTokenRefresh(10000);
461+
getAuthManager().scheduleAuthTokenRefresh(authManager.getNextRetryInterval(), true, null);
461462
}
462463
}
463464
}
@@ -697,6 +698,17 @@ public IterableAttributionInfo getAttributionInfo() {
697698
);
698699
}
699700

701+
/**
702+
* // This method gets called from developer end only.
703+
* @param pauseRetry to pause/unpause auth retries
704+
*/
705+
public void pauseAuthRetries(boolean pauseRetry) {
706+
getAuthManager().pauseAuthRetries(pauseRetry);
707+
if (!pauseRetry) { // request new auth token as soon as unpause
708+
getAuthManager().requestNewAuthToken(false);
709+
}
710+
}
711+
700712
public void setEmail(@Nullable String email) {
701713
setEmail(email, null, null, null);
702714
}

iterableapi/src/main/java/com/iterable/iterableapi/IterableAuthManager.java

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.iterable.iterableapi;
22

33
import android.util.Base64;
4-
54
import androidx.annotation.VisibleForTesting;
65

76
import org.json.JSONException;
@@ -20,23 +19,46 @@ public class IterableAuthManager {
2019
private final IterableApi api;
2120
private final IterableAuthHandler authHandler;
2221
private final long expiringAuthTokenRefreshPeriod;
23-
private final long scheduledRefreshPeriod = 10000;
2422
@VisibleForTesting
2523
Timer timer;
2624
private boolean hasFailedPriorAuth;
2725
private boolean pendingAuth;
2826
private boolean requiresAuthRefresh;
27+
RetryPolicy authRetryPolicy;
28+
boolean pauseAuthRetry;
29+
int retryCount;
30+
private boolean isLastAuthTokenValid;
31+
private boolean isTimerScheduled;
2932

3033
private final ExecutorService executor = Executors.newSingleThreadExecutor();
3134

32-
IterableAuthManager(IterableApi api, IterableAuthHandler authHandler, long expiringAuthTokenRefreshPeriod) {
35+
IterableAuthManager(IterableApi api, IterableAuthHandler authHandler, RetryPolicy authRetryPolicy, long expiringAuthTokenRefreshPeriod) {
3336
this.api = api;
3437
this.authHandler = authHandler;
38+
this.authRetryPolicy = authRetryPolicy;
3539
this.expiringAuthTokenRefreshPeriod = expiringAuthTokenRefreshPeriod;
3640
}
3741

3842
public synchronized void requestNewAuthToken(boolean hasFailedPriorAuth) {
39-
requestNewAuthToken(hasFailedPriorAuth, null);
43+
requestNewAuthToken(hasFailedPriorAuth, null, true);
44+
}
45+
46+
public void pauseAuthRetries(boolean pauseRetry) {
47+
pauseAuthRetry = pauseRetry;
48+
resetRetryCount();
49+
}
50+
51+
void reset() {
52+
clearRefreshTimer();
53+
setIsLastAuthTokenValid(false);
54+
}
55+
56+
void setIsLastAuthTokenValid(boolean isValid) {
57+
isLastAuthTokenValid = isValid;
58+
}
59+
60+
void resetRetryCount() {
61+
retryCount = 0;
4062
}
4163

4264
private void handleSuccessForAuthToken(String authToken, IterableHelper.SuccessHandler successCallback) {
@@ -51,7 +73,12 @@ private void handleSuccessForAuthToken(String authToken, IterableHelper.SuccessH
5173

5274
public synchronized void requestNewAuthToken(
5375
boolean hasFailedPriorAuth,
54-
final IterableHelper.SuccessHandler successCallback) {
76+
final IterableHelper.SuccessHandler successCallback,
77+
boolean shouldIgnoreRetryPolicy) {
78+
if ((!shouldIgnoreRetryPolicy && pauseAuthRetry) || (retryCount >= authRetryPolicy.maxRetry && !shouldIgnoreRetryPolicy)) {
79+
return;
80+
}
81+
5582
if (authHandler != null) {
5683
if (!pendingAuth) {
5784
if (!(this.hasFailedPriorAuth && hasFailedPriorAuth)) {
@@ -62,9 +89,17 @@ public synchronized void requestNewAuthToken(
6289
@Override
6390
public void run() {
6491
try {
92+
if (isLastAuthTokenValid && !shouldIgnoreRetryPolicy) {
93+
// if some JWT retry had valid token it will not fetch the auth token again from developer function
94+
handleAuthTokenSuccess(IterableApi.getInstance().getAuthToken(), successCallback);
95+
return;
96+
}
6597
final String authToken = authHandler.onAuthTokenRequested();
98+
pendingAuth = false;
99+
retryCount++;
66100
handleAuthTokenSuccess(authToken, successCallback);
67101
} catch (final Exception e) {
102+
retryCount++;
68103
handleAuthTokenFailure(e);
69104
}
70105
}
@@ -89,12 +124,11 @@ private void handleAuthTokenSuccess(String authToken, IterableHelper.SuccessHand
89124
} else {
90125
IterableLogger.w(TAG, "Auth token received as null. Calling the handler in 10 seconds");
91126
//TODO: Make this time configurable and in sync with SDK initialization flow for auth null scenario
92-
scheduleAuthTokenRefresh(scheduledRefreshPeriod);
127+
scheduleAuthTokenRefresh(getNextRetryInterval(), false, null);
93128
authHandler.onTokenRegistrationFailed(new Throwable("Auth token null"));
94129
return;
95130
}
96131
IterableApi.getInstance().setAuthToken(authToken);
97-
pendingAuth = false;
98132
reSyncAuth();
99133
authHandler.onTokenRegistrationSuccessful(authToken);
100134
}
@@ -103,7 +137,7 @@ private void handleAuthTokenFailure(Throwable throwable) {
103137
IterableLogger.e(TAG, "Error while requesting Auth Token", throwable);
104138
authHandler.onTokenRegistrationFailed(throwable);
105139
pendingAuth = false;
106-
reSyncAuth();
140+
scheduleAuthTokenRefresh(getNextRetryInterval(), false, null);
107141
}
108142

109143
public void queueExpirationRefresh(String encodedJWT) {
@@ -112,15 +146,15 @@ public void queueExpirationRefresh(String encodedJWT) {
112146
long expirationTimeSeconds = decodedExpiration(encodedJWT);
113147
long triggerExpirationRefreshTime = expirationTimeSeconds * 1000L - expiringAuthTokenRefreshPeriod - IterableUtil.currentTimeMillis();
114148
if (triggerExpirationRefreshTime > 0) {
115-
scheduleAuthTokenRefresh(triggerExpirationRefreshTime);
149+
scheduleAuthTokenRefresh(triggerExpirationRefreshTime, true, null);
116150
} else {
117151
IterableLogger.w(TAG, "The expiringAuthTokenRefreshPeriod has already passed for the current JWT");
118152
}
119153
} catch (Exception e) {
120154
IterableLogger.e(TAG, "Error while parsing JWT for the expiration", e);
121155
authHandler.onTokenRegistrationFailed(new Throwable("Auth token decode failure. Scheduling auth token refresh in 10 seconds..."));
122156
//TODO: Sync with configured time duration once feature is available.
123-
scheduleAuthTokenRefresh(scheduledRefreshPeriod);
157+
scheduleAuthTokenRefresh(getNextRetryInterval(), false, null);
124158
}
125159
}
126160

@@ -131,28 +165,57 @@ void resetFailedAuth() {
131165
void reSyncAuth() {
132166
if (requiresAuthRefresh) {
133167
requiresAuthRefresh = false;
134-
requestNewAuthToken(false);
168+
scheduleAuthTokenRefresh(getNextRetryInterval(), false, null);
135169
}
136170
}
137171

138-
void scheduleAuthTokenRefresh(long timeDuration) {
139-
timer = new Timer(true);
172+
long getNextRetryInterval() {
173+
long nextRetryInterval = authRetryPolicy.retryInterval;
174+
if (authRetryPolicy.retryBackoff == RetryPolicy.Type.EXPONENTIAL) {
175+
nextRetryInterval *= Math.pow(IterableConstants.EXPONENTIAL_FACTOR, retryCount - 1); // Exponential backoff
176+
}
177+
178+
return nextRetryInterval;
179+
}
180+
181+
void scheduleAuthTokenRefresh(long timeDuration, boolean isScheduledRefresh, final IterableHelper.SuccessHandler successCallback) {
182+
if ((pauseAuthRetry && !isScheduledRefresh) || isTimerScheduled) {
183+
// we only stop schedule token refresh if it is called from retry (in case of failure). The normal auth token refresh schedule would work
184+
return;
185+
}
186+
if (timer == null) {
187+
timer = new Timer(true);
188+
}
140189
try {
141190
timer.schedule(new TimerTask() {
142191
@Override
143192
public void run() {
144193
if (api.getEmail() != null || api.getUserId() != null) {
145-
api.getAuthManager().requestNewAuthToken(false);
194+
api.getAuthManager().requestNewAuthToken(false, successCallback, isScheduledRefresh);
146195
} else {
147196
IterableLogger.w(TAG, "Email or userId is not available. Skipping token refresh");
148197
}
198+
isTimerScheduled = false;
149199
}
150200
}, timeDuration);
201+
isTimerScheduled = true;
151202
} catch (Exception e) {
152203
IterableLogger.e(TAG, "timer exception: " + timer, e);
153204
}
154205
}
155206

207+
private String getEmailOrUserId() {
208+
String email = api.getEmail();
209+
String userId = api.getUserId();
210+
211+
if (email != null) {
212+
return email;
213+
} else if (userId != null) {
214+
return userId;
215+
}
216+
return null;
217+
}
218+
156219
private static long decodedExpiration(String encodedJWT) throws Exception {
157220
long exp = 0;
158221
String[] split = encodedJWT.split("\\.");

iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ public class IterableConfig {
6565
*/
6666
final long expiringAuthTokenRefreshPeriod;
6767

68+
/**
69+
* Retry policy for JWT Refresh.
70+
*/
71+
final RetryPolicy retryPolicy;
72+
6873
/**
6974
* By default, the SDK allows navigation/calls to URLs with the `https` protocol (e.g. deep links or external links)
7075
* If you'd like to allow other protocols like `http`, `tel`, etc., add them to the `allowedProtocols` array
@@ -100,6 +105,7 @@ private IterableConfig(Builder builder) {
100105
inAppDisplayInterval = builder.inAppDisplayInterval;
101106
authHandler = builder.authHandler;
102107
expiringAuthTokenRefreshPeriod = builder.expiringAuthTokenRefreshPeriod;
108+
retryPolicy = builder.retryPolicy;
103109
allowedProtocols = builder.allowedProtocols;
104110
dataRegion = builder.dataRegion;
105111
useInMemoryStorageForInApps = builder.useInMemoryStorageForInApps;
@@ -118,6 +124,7 @@ public static class Builder {
118124
private double inAppDisplayInterval = 30.0;
119125
private IterableAuthHandler authHandler;
120126
private long expiringAuthTokenRefreshPeriod = 60000L;
127+
private RetryPolicy retryPolicy = new RetryPolicy(10, 6L, RetryPolicy.Type.LINEAR);
121128
private String[] allowedProtocols = new String[0];
122129
private IterableDataRegion dataRegion = IterableDataRegion.US;
123130
private boolean useInMemoryStorageForInApps = false;
@@ -224,6 +231,16 @@ public Builder setAuthHandler(@NonNull IterableAuthHandler authHandler) {
224231
return this;
225232
}
226233

234+
/**
235+
* Set retry policy for JWT Refresh
236+
* @param retryPolicy
237+
*/
238+
@NonNull
239+
public Builder setAuthRetryPolicy(@NonNull RetryPolicy retryPolicy) {
240+
this.retryPolicy = retryPolicy;
241+
return this;
242+
}
243+
227244
/**
228245
* Set a custom period before an auth token expires to automatically retrieve a new token
229246
* @param period in seconds

iterableapi/src/main/java/com/iterable/iterableapi/IterableConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ public final class IterableConstants {
260260
public static final int ITERABLE_IN_APP_ANIMATION_DURATION = 500;
261261
public static final int ITERABLE_IN_APP_BACKGROUND_ANIMATION_DURATION = 300;
262262

263+
public static final int EXPONENTIAL_FACTOR = 2;
264+
263265
public static final double ITERABLE_IN_APP_PRIORITY_LEVEL_LOW = 400.0;
264266
public static final double ITERABLE_IN_APP_PRIORITY_LEVEL_MEDIUM = 300.0;
265267
public static final double ITERABLE_IN_APP_PRIORITY_LEVEL_HIGH = 200.0;

0 commit comments

Comments
 (0)