Skip to content

Commit 98f3360

Browse files
@W-15993541/W-15993636: [Android] Publish AILTN logs on background/Register push notifications on foreground (#2587)
1 parent 5922631 commit 98f3360

5 files changed

Lines changed: 280 additions & 78 deletions

File tree

libs/SalesforceSDK/src/com/salesforce/androidsdk/analytics/AnalyticsPublishingWorker.kt

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,35 @@
2727
package com.salesforce.androidsdk.analytics
2828

2929
import android.content.Context
30+
import androidx.work.Constraints
3031
import androidx.work.ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE
3132
import androidx.work.ListenableWorker.Result.success
32-
import androidx.work.PeriodicWorkRequest.Builder
33+
import androidx.work.NetworkType.CONNECTED
34+
import androidx.work.OneTimeWorkRequest
35+
import androidx.work.PeriodicWorkRequest
3336
import androidx.work.WorkManager.getInstance
3437
import androidx.work.Worker
3538
import androidx.work.WorkerParameters
3639
import com.salesforce.androidsdk.accounts.UserAccountManager
37-
import com.salesforce.androidsdk.analytics.AnalyticsPublishingWorker.Companion.reEnqueueAnalyticsPublishPeriodicWorkRequest
40+
import com.salesforce.androidsdk.analytics.AnalyticsPublishingWorker.Companion.enqueueAnalyticsPublishWorkRequest
3841
import java.util.concurrent.TimeUnit.HOURS
3942

4043
/**
4144
* An Android background tasks worker which publishes stored analytics.
4245
* This class is intended to be instantiated by the background tasks work
4346
* manager.
4447
*
45-
* Use [reEnqueueAnalyticsPublishPeriodicWorkRequest] to enqueue a analytics
46-
* publish periodic work request. Previous requests will be cancelled.
48+
* [enqueueAnalyticsPublishWorkRequest] is used internally by the Salesforce
49+
* Mobile SDK to enqueue analytics publish work requests to match the current
50+
* analytics publishing configuration. Only one request may be active at a time.
51+
* Only the Salesforce Mobile SDK should call this method as it is not intended
52+
* for public use.
4753
*
4854
* @param context The Android context provided by the work manager
4955
* @param workerParams The worker parameters provided by the work manager
56+
* @see [SalesforceAnalyticsManager.setPublishOnceTimeOnAppBackgroundEnabled]
57+
* @see [SalesforceAnalyticsManager.setPublishPeriodicallyOnFrequencyEnabled]
58+
* @see [SalesforceAnalyticsManager.setPublishPeriodicallyFrequencyHours]
5059
* @see <a href='https://developer.android.com/guide/background'>Android
5160
* Background Tasks</a>
5261
*/
@@ -73,36 +82,67 @@ internal class AnalyticsPublishingWorker(
7382

7483
companion object {
7584

76-
private const val publishAnalyticsPeriodicWorkName = "SalesforceAnalyticsPublishingPeriodicWork"
85+
/**
86+
* The Android background tasks name of the publish analytics work
87+
* request.
88+
* Note: This name is also used for one-time work, yet maintains this
89+
* value for backwards compatibility and to ensure both periodic and one
90+
* time work are mutually exclusive.
91+
*/
92+
private const val PUBLISH_ANALYTICS_WORK_NAME = "SalesforceAnalyticsPublishingPeriodicWork"
7793

7894
/**
79-
* Enqueues a persistent background tasks periodic work request to
80-
* publish stored analytics for the current user at the provided
81-
* interval.
95+
* Enqueues a persistent background tasks work request to publish stored
96+
* analytics for the current user.
8297
*
8398
* If a work request is already queued, it will be cancelled before
8499
* the replacement is enqueued.
85100
*
101+
* The publish hours interval parameter determines between background
102+
* periodic publishing and one-time publishing. Note, background
103+
* periodic publishing starts or resumes the host app and may incur
104+
* licensing costs if authorization token refresh is required.
105+
*
106+
* Only the Salesforce Mobile SDK should call this method as it is not
107+
* intended for public use.
108+
*
86109
* @param context The Android context
87-
* @param publishHoursInterval The interval at which to publish in hours
110+
* @param periodicBackgroundPublishingHoursInterval The interval for
111+
* periodic background publishing in hours or null to publish one time
112+
* only
88113
* @return UUID The worker's unique id, which may be used for
89114
* cancellation
90115
*/
91-
fun reEnqueueAnalyticsPublishPeriodicWorkRequest(
116+
fun enqueueAnalyticsPublishWorkRequest(
92117
context: Context,
93-
publishHoursInterval: Long
94-
) = Builder(
95-
AnalyticsPublishingWorker::class.java,
96-
publishHoursInterval,
97-
HOURS
98-
).build().also { publishAnalyticsPeriodicWorkRequest ->
99-
runCatching {
100-
getInstance(context)
101-
}.getOrNull()?.enqueueUniquePeriodicWork(
102-
publishAnalyticsPeriodicWorkName,
103-
CANCEL_AND_REENQUEUE,
104-
publishAnalyticsPeriodicWorkRequest
105-
)
106-
}.id
118+
periodicBackgroundPublishingHoursInterval: Long? = null
119+
) = when (periodicBackgroundPublishingHoursInterval) {
120+
121+
null -> OneTimeWorkRequest.Builder(
122+
AnalyticsPublishingWorker::class.java
123+
).setConstraints(
124+
Constraints.Builder().setRequiredNetworkType(CONNECTED).build()
125+
).build().also { publishAnalyticsOneTimeWorkRequest ->
126+
runCatching {
127+
getInstance(context)
128+
}.getOrNull()?.enqueue(publishAnalyticsOneTimeWorkRequest)
129+
}.id
130+
131+
else -> PeriodicWorkRequest.Builder(
132+
AnalyticsPublishingWorker::class.java,
133+
periodicBackgroundPublishingHoursInterval,
134+
HOURS
135+
).setConstraints(
136+
Constraints.Builder().setRequiredNetworkType(CONNECTED).build()
137+
).build().also { publishAnalyticsPeriodicWorkRequest ->
138+
runCatching {
139+
getInstance(context)
140+
}.getOrNull()?.enqueueUniquePeriodicWork(
141+
PUBLISH_ANALYTICS_WORK_NAME,
142+
CANCEL_AND_REENQUEUE,
143+
publishAnalyticsPeriodicWorkRequest
144+
)
145+
}.id
146+
}
107147
}
108148
}

libs/SalesforceSDK/src/com/salesforce/androidsdk/analytics/SalesforceAnalyticsManager.java

Lines changed: 100 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ public class SalesforceAnalyticsManager {
7070
private static final String UNAUTH_INSTANCE_KEY = "_no_user";
7171

7272
private static Map<String, SalesforceAnalyticsManager> INSTANCES;
73-
private static boolean sPublishHandlerActive;
74-
private static int sPublishFrequencyInHours = DEFAULT_PUBLISH_FREQUENCY_IN_HOURS;
73+
private static boolean isPublishWorkRequestEnqueued;
74+
private static boolean isPublishOneTimeOnAppBackgroundEnabled = true;
75+
private static boolean isPublishPeriodicallyOnFrequencyEnabled = false;
76+
private static int publishPeriodicallyFrequencyHours = DEFAULT_PUBLISH_FREQUENCY_IN_HOURS;
7577
private static int sEventPublishBatchSize = DEFAULT_BATCH_SIZE;
7678

7779
private final AnalyticsManager analyticsManager;
@@ -102,7 +104,7 @@ public static synchronized SalesforceAnalyticsManager getInstance(UserAccount ac
102104
/**
103105
* Returns the instance of this class associated with this user and community.
104106
*
105-
* @param account User account.
107+
* @param account User account.
106108
* @param communityId Community ID.
107109
* @return Instance of this class.
108110
*/
@@ -131,9 +133,9 @@ public static synchronized SalesforceAnalyticsManager getInstance(UserAccount ac
131133
}
132134

133135
// Adds a handler for publishing if not already active.
134-
if (!sPublishHandlerActive) {
135-
recreateAnalyticsPublishPeriodicWorkRequest();
136-
sPublishHandlerActive = true;
136+
if (!isPublishWorkRequestEnqueued) {
137+
recreateAnalyticsPeriodicBackgroundPublishingWorkRequest();
138+
isPublishWorkRequestEnqueued = true;
137139
}
138140
return instance;
139141
}
@@ -157,7 +159,7 @@ public static synchronized void reset(UserAccount account) {
157159
/**
158160
* Resets the instance of this class associated with this user and community.
159161
*
160-
* @param account User account.
162+
* @param account User account.
161163
* @param communityId Community ID.
162164
*/
163165
public static synchronized void reset(UserAccount account, String communityId) {
@@ -182,13 +184,80 @@ public static synchronized void reset(UserAccount account, String communityId) {
182184
}
183185

184186
/**
185-
* Sets the publish frequency, in hours.
187+
* Sets the interval for periodic background publishing in hours.
188+
*
189+
* @param periodicBackgroundPublishingHoursInterval The interval for
190+
* periodic background
191+
* publishing in hours. It
192+
* is recommended to keep
193+
* this value under seven
194+
* days
195+
* @see #setPublishPeriodicallyOnFrequencyEnabled(boolean)
196+
*/
197+
public static synchronized void setPublishPeriodicallyFrequencyHours(
198+
int periodicBackgroundPublishingHoursInterval
199+
) {
200+
SalesforceAnalyticsManager.publishPeriodicallyFrequencyHours = periodicBackgroundPublishingHoursInterval;
201+
setPublishPeriodicallyOnFrequencyEnabled(true);
202+
}
203+
204+
/**
205+
* Sets the interval for periodic background publishing in hours.
206+
*
207+
* @deprecated Planned for removal 13.0.
208+
* Use {@link #setPublishPeriodicallyFrequencyHours(int)} )}.
209+
*/
210+
@Deprecated()
211+
public static synchronized void setPublishFrequencyInHours(
212+
int periodicBackgroundPublishingHoursInterval
213+
) {
214+
setPublishPeriodicallyFrequencyHours(periodicBackgroundPublishingHoursInterval);
215+
}
216+
217+
/**
218+
* Specifies if analytics publishing should occur one time when the app is
219+
* sent to the background.
220+
*/
221+
public static boolean isPublishOnceTimeOnAppBackgroundEnabled() {
222+
return isPublishOneTimeOnAppBackgroundEnabled;
223+
}
224+
225+
/**
226+
* Specifies if analytics publishing should occur one time when the app is
227+
* sent to the background.
228+
* <p/>
229+
* This is mutually exclusive with
230+
* [setPublishPeriodicallyOnFrequencyEnabled]
231+
*
232+
* @param value True to enable. False to disable
233+
*/
234+
public static void setPublishOnceTimeOnAppBackgroundEnabled(boolean value) {
235+
isPublishOneTimeOnAppBackgroundEnabled = value;
236+
}
237+
238+
/**
239+
* Specifies if analytics publishing should occur periodically as an
240+
* Android Background Task according to the frequency.
241+
*
242+
* @noinspection unused
243+
* @see #setPublishPeriodicallyFrequencyHours
244+
*/
245+
public static boolean isPublishPeriodicallyOnFrequencyEnabled() {
246+
return isPublishPeriodicallyOnFrequencyEnabled;
247+
}
248+
249+
/**
250+
* Specifies if analytics publishing should occur periodically as an
251+
* Android Background Task according to the frequency.
252+
* <p>
253+
* This is mutually exclusive with
254+
* [setPublishOnceTimeOnAppBackgroundEnabled]
186255
*
187-
* @param publishFrequencyInHours Publish frequency, in hours.
256+
* @param value True to enable. False to disable
257+
* @see #setPublishPeriodicallyFrequencyHours
188258
*/
189-
public static synchronized void setPublishFrequencyInHours(int publishFrequencyInHours) {
190-
sPublishFrequencyInHours = publishFrequencyInHours;
191-
recreateAnalyticsPublishPeriodicWorkRequest();
259+
public static void setPublishPeriodicallyOnFrequencyEnabled(boolean value) {
260+
isPublishPeriodicallyOnFrequencyEnabled = value;
192261
}
193262

194263
/**
@@ -209,9 +278,23 @@ public static synchronized void setEventPublishBatchSize(int batchSize) {
209278
* Returns the publish frequency currently set, in hours.
210279
*
211280
* @return Publish frequency, in hours.
281+
* @noinspection unused
212282
*/
283+
public static int getPublishPeriodicallyFrequencyHours() {
284+
return publishPeriodicallyFrequencyHours;
285+
}
286+
287+
/**
288+
* Returns the publish frequency currently set, in hours.
289+
*
290+
* @return Publish frequency, in hours.
291+
* @noinspection unused
292+
* @deprecated Planned for removal 13.0.
293+
* Use {@link #getPublishPeriodicallyFrequencyHours()} )}.
294+
*/
295+
@Deprecated
213296
public static int getPublishFrequencyInHours() {
214-
return sPublishFrequencyInHours;
297+
return getPublishPeriodicallyFrequencyHours();
215298
}
216299

217300
/**
@@ -295,6 +378,7 @@ public synchronized void publishEvents(Iterable<InstrumentationEvent> events) {
295378
if (events == null) {
296379
return;
297380
}
381+
298382
final Set<String> eventsIds = new HashSet<>();
299383
boolean success = true;
300384
final Set<Map.Entry<Class<? extends Transform>, Class<? extends AnalyticsPublisher>>> remoteKeySet = remotes.entrySet();
@@ -474,10 +558,10 @@ private void resetAnalyticsPolicy() {
474558
e.commit();
475559
}
476560

477-
private static void recreateAnalyticsPublishPeriodicWorkRequest() {
478-
AnalyticsPublishingWorker.Companion.reEnqueueAnalyticsPublishPeriodicWorkRequest(
561+
private static void recreateAnalyticsPeriodicBackgroundPublishingWorkRequest() {
562+
AnalyticsPublishingWorker.Companion.enqueueAnalyticsPublishWorkRequest(
479563
SalesforceSDKManager.getInstance().getAppContext(),
480-
sPublishFrequencyInHours
564+
(long) publishPeriodicallyFrequencyHours
481565
);
482566
}
483567
}

libs/SalesforceSDK/src/com/salesforce/androidsdk/app/SalesforceSDKManager.kt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import com.salesforce.androidsdk.R.style.SalesforceSDK_AlertDialog_Dark
7474
import com.salesforce.androidsdk.accounts.UserAccount
7575
import com.salesforce.androidsdk.accounts.UserAccountManager
7676
import com.salesforce.androidsdk.accounts.UserAccountManager.USER_SWITCH_TYPE_LOGOUT
77+
import com.salesforce.androidsdk.analytics.AnalyticsPublishingWorker.Companion.enqueueAnalyticsPublishWorkRequest
7778
import com.salesforce.androidsdk.analytics.EventBuilderHelper.createAndStoreEvent
7879
import com.salesforce.androidsdk.analytics.SalesforceAnalyticsManager
7980
import com.salesforce.androidsdk.analytics.security.Encryptor
@@ -105,9 +106,11 @@ import com.salesforce.androidsdk.developer.support.notifications.local.ShowDevel
105106
import com.salesforce.androidsdk.push.PushMessaging
106107
import com.salesforce.androidsdk.push.PushMessaging.UNREGISTERED_ATTEMPT_COMPLETE_EVENT
107108
import com.salesforce.androidsdk.push.PushMessaging.isRegistered
109+
import com.salesforce.androidsdk.push.PushMessaging.register
108110
import com.salesforce.androidsdk.push.PushMessaging.unregister
109111
import com.salesforce.androidsdk.push.PushNotificationInterface
110112
import com.salesforce.androidsdk.push.PushService
113+
import com.salesforce.androidsdk.push.PushService.Companion.isPushNotificationsRegistrationOneTimeOnAppForegroundEnabled
111114
import com.salesforce.androidsdk.rest.ClientManager
112115
import com.salesforce.androidsdk.rest.ClientManager.LoginOptions
113116
import com.salesforce.androidsdk.rest.RestClient
@@ -898,7 +901,12 @@ open class SalesforceSDKManager protected constructor(
898901
frontActivity: Activity?,
899902
showLoginPage: Boolean = true,
900903
) {
901-
logout(account, frontActivity, showLoginPage)
904+
logout(
905+
account = account,
906+
frontActivity = frontActivity,
907+
showLoginPage = showLoginPage,
908+
reason = LogoutReason.UNKNOWN
909+
)
902910
}
903911

904912
// Note the below overload exists because @JvmOverloads generates non-overrideable
@@ -1451,6 +1459,14 @@ open class SalesforceSDKManager protected constructor(
14511459
@OnLifecycleEvent(ON_STOP)
14521460
protected open fun onAppBackgrounded() {
14531461
screenLockManager?.onAppBackgrounded()
1462+
1463+
// Publish analytics one-time on app background, if enabled.
1464+
if (SalesforceAnalyticsManager.isPublishOnceTimeOnAppBackgroundEnabled()) {
1465+
enqueueAnalyticsPublishWorkRequest(
1466+
getInstance().appContext
1467+
)
1468+
}
1469+
14541470
(biometricAuthenticationManager as? BiometricAuthenticationManager)?.onAppBackgrounded()
14551471

14561472
// Hide the Salesforce Mobile SDK "Show Developer Support" notification
@@ -1465,6 +1481,17 @@ open class SalesforceSDKManager protected constructor(
14651481
screenLockManager?.onAppForegrounded()
14661482
(biometricAuthenticationManager as? BiometricAuthenticationManager)?.onAppForegrounded()
14671483

1484+
// Review push-notifications registration for the current user, if enabled.
1485+
userAccountManager.currentUser?.let { userAccount ->
1486+
if (isPushNotificationsRegistrationOneTimeOnAppForegroundEnabled) {
1487+
register(
1488+
context = appContext,
1489+
account = userAccount,
1490+
recreateKey = false
1491+
)
1492+
}
1493+
}
1494+
14681495
// Display the Salesforce Mobile SDK "Show Developer Support" notification
14691496
if (userAccountManager.currentAccount != null && authenticatedActivityForDeveloperSupport != null) {
14701497
showDeveloperSupportNotification(

0 commit comments

Comments
 (0)