Skip to content

Commit 5823bee

Browse files
Merge branch 'trunk' into issue/WOOMOB-1126-app-passwords-network
2 parents b43d54c + 2c804fa commit 5823bee

File tree

48 files changed

+937
-617
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+937
-617
lines changed

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/jetpack/JetpackActivationRepository.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,9 @@ class JetpackActivationRepository @Inject constructor(
165165
WooLog.d(WooLog.T.LOGIN, "Jetpack Activation: Site $siteUrl is missing from account sites")
166166
Result.failure(IllegalStateException("Site missing"))
167167
} else {
168-
if (!site.hasWooCommerce) {
169-
// If the site doesn't have WooCommerce, let's do one additional fetch using `fetchSite`,
170-
// this function will make sure to fetch data from the remote site, which might result in more
171-
// accurate result
172-
siteStore.fetchSite(site)
173-
}
168+
// Fetch full site details from the remote site, this will also allow us to fetch the
169+
// Applications Passwords support on the site
170+
siteStore.fetchSite(site)
174171
Result.success(site)
175172
}
176173
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ import com.woocommerce.android.ui.woopos.home.cart.WooPosCartScreen
3737
import com.woocommerce.android.ui.woopos.home.cart.WooPosCartScreenProductsPreview
3838
import com.woocommerce.android.ui.woopos.home.items.WooPosItemsScreen
3939
import com.woocommerce.android.ui.woopos.home.items.products.WooPosItemsScreenPreview
40-
import com.woocommerce.android.ui.woopos.home.scanningsetup.WooPosScanningSetupDialog
4140
import com.woocommerce.android.ui.woopos.home.toolbar.PreviewWooPosFloatingToolbarStatusConnectedWithMenu
4241
import com.woocommerce.android.ui.woopos.home.toolbar.WooPosFloatingToolbar
4342
import com.woocommerce.android.ui.woopos.home.totals.WooPosTotalsScreen
4443
import com.woocommerce.android.ui.woopos.home.totals.WooPosTotalsScreenPreview
44+
import com.woocommerce.android.ui.woopos.scanningsetup.WooPosScanningSetupDialog
4545
import org.wordpress.android.util.ToastUtils
4646

4747
@Composable
Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.woocommerce.android.ui.woopos.localcatalog
22

3+
import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper
4+
import com.woocommerce.android.ui.woopos.localcatalog.WooPosSyncProductsAction.WooPosSyncProductsResult
35
import com.woocommerce.android.ui.woopos.util.datastore.WooPosSyncTimestampManager
46
import com.woocommerce.android.util.CoroutineDispatchers
5-
import com.woocommerce.android.util.WooLog
6-
import com.woocommerce.android.util.WooLog.T
77
import kotlinx.coroutines.withContext
88
import org.wordpress.android.fluxc.model.SiteModel
99
import javax.inject.Inject
@@ -24,9 +24,10 @@ sealed class PosLocalCatalogSyncResult {
2424

2525
@Singleton
2626
class PosLocalCatalogSyncRepository @Inject constructor(
27-
private val posSyncProductsAction: PosSyncProductsAction,
27+
private val posSyncProductsAction: WooPosSyncProductsAction,
2828
private val syncTimestampManager: WooPosSyncTimestampManager,
2929
private val dispatchers: CoroutineDispatchers,
30+
private val logger: WooPosLogWrapper,
3031
) {
3132
companion object {
3233
const val PAGE_SIZE = 100
@@ -58,15 +59,15 @@ class PosLocalCatalogSyncRepository @Inject constructor(
5859
): PosLocalCatalogSyncResult {
5960
val startTime = System.currentTimeMillis()
6061

61-
WooLog.d(T.POS, "Starting sync for items modified after $modifiedAfterGmt, max pages: $maxPages")
62+
logger.d("Starting sync for items modified after $modifiedAfterGmt, max pages: $maxPages")
6263

6364
val productSyncResult = posSyncProductsAction.execute(site, modifiedAfterGmt, pageSize, maxPages)
6465
// TBD Local Catalog We'll want to trigger variations action here too
6566

6667
val syncDuration = System.currentTimeMillis() - startTime
6768

6869
return when (productSyncResult) {
69-
is PosSyncProductsAction.Result.Success -> {
70+
is WooPosSyncProductsResult.Success -> {
7071
// TBD Local Catalog we need to use store server timestamp
7172
val currentTime = System.currentTimeMillis()
7273
// TBD Local Catalog We need to store incremental and full sync timestamps separately
@@ -79,7 +80,7 @@ class PosLocalCatalogSyncRepository @Inject constructor(
7980
)
8081
}
8182

82-
is PosSyncProductsAction.Result.Failed.CatalogTooLarge -> {
83+
is WooPosSyncProductsResult.Failed.CatalogTooLarge -> {
8384
PosLocalCatalogSyncResult.Failure.CatalogTooLarge(
8485
error = "Catalog too large: ${productSyncResult.totalPages} pages exceed maximum " +
8586
"of ${productSyncResult.maxPages} pages",
@@ -88,7 +89,7 @@ class PosLocalCatalogSyncRepository @Inject constructor(
8889
)
8990
}
9091

91-
is PosSyncProductsAction.Result.Failed -> {
92+
is WooPosSyncProductsResult.Failed -> {
9293
PosLocalCatalogSyncResult.Failure.UnexpectedError(productSyncResult.error)
9394
}
9495
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.woocommerce.android.ui.woopos.localcatalog
2+
3+
import android.content.Context
4+
import androidx.work.BackoffPolicy
5+
import androidx.work.Constraints
6+
import androidx.work.ExistingPeriodicWorkPolicy
7+
import androidx.work.ExistingWorkPolicy
8+
import androidx.work.NetworkType
9+
import androidx.work.OneTimeWorkRequestBuilder
10+
import androidx.work.PeriodicWorkRequestBuilder
11+
import androidx.work.WorkInfo
12+
import androidx.work.WorkManager
13+
import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper
14+
import dagger.hilt.android.qualifiers.ApplicationContext
15+
import java.util.Calendar
16+
import java.util.concurrent.TimeUnit
17+
import javax.inject.Inject
18+
import javax.inject.Singleton
19+
20+
@Singleton
21+
class WooPosLocalCatalogSyncScheduler @Inject constructor(
22+
@ApplicationContext private val context: Context,
23+
private val logger: WooPosLogWrapper,
24+
) {
25+
26+
private companion object {
27+
private const val ONE_TIME_WORK_NAME = "PosLocalCatalogSyncOneTime"
28+
29+
const val REFRESH_INTERVAL_HOURS = 24L
30+
const val TIME_OF_DAY_FOR_PERIODIC_SYNC = 23 // 11 PM
31+
}
32+
33+
private val workManager = WorkManager.getInstance(context)
34+
35+
fun schedulePeriodicFullCatalogSync() {
36+
val syncWorkRequest = PeriodicWorkRequestBuilder<WooPosLocalCatalogSyncWorker>(
37+
REFRESH_INTERVAL_HOURS,
38+
TimeUnit.HOURS
39+
)
40+
.setInitialDelay(calculateDelayToNight(), TimeUnit.MILLISECONDS)
41+
.setConstraints(getConstraints())
42+
.setBackoffCriteria(
43+
BackoffPolicy.EXPONENTIAL,
44+
1,
45+
TimeUnit.MINUTES
46+
)
47+
.build()
48+
49+
workManager.enqueueUniquePeriodicWork(
50+
WooPosLocalCatalogSyncWorker.WORK_NAME,
51+
ExistingPeriodicWorkPolicy.KEEP,
52+
syncWorkRequest
53+
)
54+
55+
logger.d("POS local catalog full sync scheduled.")
56+
}
57+
58+
fun triggerManualFullCatalogSync() {
59+
val oneTimeWorkRequest = OneTimeWorkRequestBuilder<WooPosLocalCatalogSyncWorker>()
60+
.setConstraints(getConstraints())
61+
.setBackoffCriteria(
62+
BackoffPolicy.EXPONENTIAL,
63+
1,
64+
TimeUnit.MINUTES
65+
)
66+
.build()
67+
68+
workManager.enqueueUniqueWork(
69+
ONE_TIME_WORK_NAME,
70+
ExistingWorkPolicy.REPLACE,
71+
oneTimeWorkRequest
72+
)
73+
74+
logger.d("Manual POS local catalog sync triggered")
75+
}
76+
77+
fun isPeriodicWorkRunning(): Boolean {
78+
val periodicWork = workManager.getWorkInfosForUniqueWork(WooPosLocalCatalogSyncWorker.WORK_NAME).get()
79+
80+
return periodicWork.any { it.state == WorkInfo.State.RUNNING }
81+
}
82+
83+
fun isOneTimeWorkRunning(): Boolean {
84+
val oneTimeWork = workManager.getWorkInfosForUniqueWork(ONE_TIME_WORK_NAME).get()
85+
86+
return oneTimeWork.any { it.state == WorkInfo.State.RUNNING }
87+
}
88+
89+
private fun getConstraints(): Constraints {
90+
return Constraints.Builder()
91+
.setRequiredNetworkType(NetworkType.CONNECTED)
92+
.setRequiresBatteryNotLow(true)
93+
.build()
94+
}
95+
96+
private fun calculateDelayToNight(): Long {
97+
val now = Calendar.getInstance()
98+
val night = Calendar.getInstance().apply {
99+
set(Calendar.HOUR_OF_DAY, TIME_OF_DAY_FOR_PERIODIC_SYNC)
100+
set(Calendar.MINUTE, 0)
101+
set(Calendar.SECOND, 0)
102+
103+
if (before(now)) add(Calendar.DAY_OF_YEAR, 1)
104+
}
105+
return night.timeInMillis - now.timeInMillis
106+
}
107+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.woocommerce.android.ui.woopos.localcatalog
2+
3+
import android.content.Context
4+
import androidx.hilt.work.HiltWorker
5+
import androidx.work.CoroutineWorker
6+
import androidx.work.WorkerParameters
7+
import com.woocommerce.android.tools.SelectedSite
8+
import com.woocommerce.android.ui.login.AccountRepository
9+
import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper
10+
import dagger.assisted.Assisted
11+
import dagger.assisted.AssistedInject
12+
13+
@HiltWorker
14+
class WooPosLocalCatalogSyncWorker @AssistedInject constructor(
15+
@Assisted appContext: Context,
16+
@Assisted workerParams: WorkerParameters,
17+
private val accountRepository: AccountRepository,
18+
private val selectedSite: SelectedSite,
19+
private val syncRepository: PosLocalCatalogSyncRepository,
20+
private val logger: WooPosLogWrapper,
21+
) : CoroutineWorker(appContext, workerParams) {
22+
23+
companion object {
24+
const val WORK_NAME = "PosLocalCatalogSyncWork"
25+
}
26+
27+
@Suppress("ReturnCount")
28+
override suspend fun doWork(): Result {
29+
if (!accountRepository.isUserLoggedIn()) {
30+
logger.d("User not logged in, skipping local catalog sync")
31+
return Result.failure()
32+
}
33+
34+
val site = selectedSite.getOrNull()
35+
if (site == null) {
36+
logger.e("No selected WooCommerce site found, skipping local catalog sync")
37+
return Result.failure()
38+
}
39+
40+
logger.d("Starting full local catalog sync")
41+
42+
val syncResult = syncRepository.syncLocalCatalogFull(site)
43+
44+
return when (syncResult) {
45+
is PosLocalCatalogSyncResult.Success -> {
46+
logger.d(
47+
"Local catalog sync completed successfully. Products: ${syncResult.productsSynced}, " +
48+
"Variations: ${syncResult.variationsSynced}, Duration: ${syncResult.syncDurationMs}ms"
49+
)
50+
Result.success()
51+
}
52+
53+
is PosLocalCatalogSyncResult.Failure.UnexpectedError -> {
54+
logger.e("Local catalog sync failed: ${syncResult.error}. Retrying ...")
55+
Result.retry()
56+
}
57+
is PosLocalCatalogSyncResult.Failure.CatalogTooLarge -> {
58+
// TBD Local Catalog - stop future syncs for this site if catalog too large
59+
logger.e("Local catalog sync failed: ${syncResult.error}.")
60+
Result.failure()
61+
}
62+
}
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
package com.woocommerce.android.ui.woopos.localcatalog
22

3-
import com.woocommerce.android.util.WooLog
4-
import com.woocommerce.android.util.WooLog.T
3+
import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper
54
import org.wordpress.android.fluxc.model.SiteModel
6-
import org.wordpress.android.fluxc.store.pos.localcatalog.PosLocalCatalogStore
5+
import org.wordpress.android.fluxc.store.pos.localcatalog.WooPosLocalCatalogStore
76
import javax.inject.Inject
87

9-
class PosSyncProductsAction @Inject constructor(
10-
private val posLocalCatalogStore: PosLocalCatalogStore
8+
class WooPosSyncProductsAction @Inject constructor(
9+
private val posLocalCatalogStore: WooPosLocalCatalogStore,
10+
private val logger: WooPosLogWrapper,
1111
) {
12-
sealed class Result {
13-
data class Success(val productsSynced: Int) : Result()
12+
sealed class WooPosSyncProductsResult {
13+
data class Success(val productsSynced: Int) : WooPosSyncProductsResult()
1414

15-
sealed class Failed(val error: String) : Result() {
15+
sealed class Failed(val error: String) : WooPosSyncProductsResult() {
1616
class CatalogTooLarge(val totalPages: Int, val maxPages: Int) :
1717
Failed("Catalog too large: $totalPages pages exceed maximum of $maxPages pages")
18+
1819
class UnexpectedError(error: String) : Failed(error)
1920
}
2021
}
@@ -25,7 +26,7 @@ class PosSyncProductsAction @Inject constructor(
2526
modifiedAfterGmt: String? = null,
2627
pageSize: Int,
2728
maxPages: Int
28-
): Result {
29+
): WooPosSyncProductsResult {
2930
var currentOffset = 0
3031
var pagesSynced = 0
3132
var totalSyncedProducts = 0
@@ -48,34 +49,33 @@ class PosSyncProductsAction @Inject constructor(
4849
// TBD Local Catalog We should first fetch the headers to decide if the catalog size is acceptable
4950
if (pagesSynced == 0) {
5051
if (syncResult.totalPages > maxPages) {
51-
WooLog.e(
52-
T.POS,
52+
logger.e(
5353
"Catalog too large: $syncResult.totalPages pages exceed maximum of $maxPages pages"
5454
)
55-
return Result.Failed.CatalogTooLarge(syncResult.totalPages, maxPages)
55+
return WooPosSyncProductsResult.Failed.CatalogTooLarge(syncResult.totalPages, maxPages)
5656
}
5757
}
5858

59-
WooLog.d(T.POS, "Page ${pagesSynced + 1} synced, ${syncResult.syncedCount} products")
59+
logger.d("Page ${pagesSynced + 1} synced, ${syncResult.syncedCount} products")
6060
totalSyncedProducts += syncResult.syncedCount
6161
pagesSynced++
6262

6363
if (!syncResult.hasMore || syncResult.syncedCount == 0) {
64-
WooLog.d(T.POS, "No more products to sync")
64+
logger.d("No more products to sync")
6565
shouldContinue = false
6666
} else {
6767
currentOffset = syncResult.nextOffset
6868
}
6969
},
7070
onFailure = { error ->
7171
// TBD Local Catalog Add retry logic. We shouldn't fail when one page fails.
72-
WooLog.e(T.POS, "Sync failed on page ${pagesSynced + 1}: ${error.message}")
73-
return Result.Failed.UnexpectedError(error.message ?: "Unknown error")
72+
logger.e("Sync failed on page ${pagesSynced + 1}: ${error.message}")
73+
return WooPosSyncProductsResult.Failed.UnexpectedError(error.message ?: "Unknown error")
7474
}
7575
)
7676
}
7777

78-
WooLog.d(T.POS, "Products sync completed, $totalSyncedProducts products synced across $pagesSynced pages")
79-
return Result.Success(totalSyncedProducts)
78+
logger.d("Products sync completed, $totalSyncedProducts products synced across $pagesSynced pages")
79+
return WooPosSyncProductsResult.Success(totalSyncedProducts)
8080
}
8181
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package com.woocommerce.android.ui.woopos.home.scanningsetup
1+
package com.woocommerce.android.ui.woopos.scanningsetup
22

33
import com.woocommerce.android.ui.woopos.common.util.ScannerInfo
44
import com.woocommerce.android.ui.woopos.common.util.WooPosScannerDetectionUtil
5-
import com.woocommerce.android.ui.woopos.home.scanningsetup.WooPosScanningSetupState.BarcodeReaderDevice
5+
import com.woocommerce.android.ui.woopos.scanningsetup.WooPosScanningSetupState.BarcodeReaderDevice
66
import kotlinx.coroutines.CoroutineScope
77
import kotlinx.coroutines.Job
88
import kotlinx.coroutines.delay
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package com.woocommerce.android.ui.woopos.home.scanningsetup
1+
package com.woocommerce.android.ui.woopos.scanningsetup
22

3-
import com.woocommerce.android.ui.woopos.home.scanningsetup.WooPosScanningSetupState.BarcodeReaderDevice
4-
import com.woocommerce.android.ui.woopos.home.scanningsetup.WooPosScanningSetupState.ScannerConfigurations
5-
import com.woocommerce.android.ui.woopos.home.scanningsetup.WooPosScanningSetupState.ScanningSetupStep
3+
import com.woocommerce.android.ui.woopos.scanningsetup.WooPosScanningSetupState.BarcodeReaderDevice
4+
import com.woocommerce.android.ui.woopos.scanningsetup.WooPosScanningSetupState.ScannerConfigurations
5+
import com.woocommerce.android.ui.woopos.scanningsetup.WooPosScanningSetupState.ScanningSetupStep
66
import javax.inject.Inject
77

88
class WooPosScannerSetupNavigator @Inject constructor() {
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
package com.woocommerce.android.ui.woopos.home.scanningsetup
1+
package com.woocommerce.android.ui.woopos.scanningsetup
22

3-
import com.woocommerce.android.ui.woopos.home.scanningsetup.WooPosScanningSetupState.BarcodeReaderDevice
4-
import com.woocommerce.android.ui.woopos.home.scanningsetup.WooPosScanningSetupState.ScanningSetupStep
3+
import com.woocommerce.android.ui.woopos.scanningsetup.WooPosScanningSetupState.BarcodeReaderDevice
4+
import com.woocommerce.android.ui.woopos.scanningsetup.WooPosScanningSetupState.ScanningSetupStep
55
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent
66
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker
77
import javax.inject.Inject

0 commit comments

Comments
 (0)