This commit:

* Uses Androidx startup auto initialization for sync
* Runs sync on app startup every time
* Indexes keys on cross reference entities

Change-Id: I4a1d948f997269a6dc4fdf78ccb14c94b789f93c
pull/2/head
Adetunji Dahunsi 3 years ago committed by Don Turner
parent 132566dc40
commit ba1de27ef3

@ -0,0 +1,448 @@
{
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "6bd08a2b063057f8fa1a1cfe31f7aca4",
"entities": [
{
"tableName": "authors",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `image_url` TEXT NOT NULL, `twitter` TEXT NOT NULL DEFAULT '', `medium_page` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "imageUrl",
"columnName": "image_url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "twitter",
"columnName": "twitter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "mediumPage",
"columnName": "medium_page",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "episodes_authors",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`episode_id` INTEGER NOT NULL, `author_id` INTEGER NOT NULL, PRIMARY KEY(`episode_id`, `author_id`), FOREIGN KEY(`episode_id`) REFERENCES `episodes`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`author_id`) REFERENCES `authors`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "episodeId",
"columnName": "episode_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authorId",
"columnName": "author_id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"episode_id",
"author_id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_episodes_authors_episode_id",
"unique": false,
"columnNames": [
"episode_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_episodes_authors_episode_id` ON `${TABLE_NAME}` (`episode_id`)"
},
{
"name": "index_episodes_authors_author_id",
"unique": false,
"columnNames": [
"author_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_episodes_authors_author_id` ON `${TABLE_NAME}` (`author_id`)"
}
],
"foreignKeys": [
{
"table": "episodes",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"episode_id"
],
"referencedColumns": [
"id"
]
},
{
"table": "authors",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"author_id"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "episodes",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `publish_date` INTEGER NOT NULL, `alternate_video` TEXT, `alternate_audio` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "publishDate",
"columnName": "publish_date",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "alternateVideo",
"columnName": "alternate_video",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "alternateAudio",
"columnName": "alternate_audio",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "news_resources_authors",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`news_resource_id` INTEGER NOT NULL, `author_id` INTEGER NOT NULL, PRIMARY KEY(`news_resource_id`, `author_id`), FOREIGN KEY(`news_resource_id`) REFERENCES `news_resources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`author_id`) REFERENCES `authors`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "newsResourceId",
"columnName": "news_resource_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authorId",
"columnName": "author_id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"news_resource_id",
"author_id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_news_resources_authors_news_resource_id",
"unique": false,
"columnNames": [
"news_resource_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_authors_news_resource_id` ON `${TABLE_NAME}` (`news_resource_id`)"
},
{
"name": "index_news_resources_authors_author_id",
"unique": false,
"columnNames": [
"author_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_authors_author_id` ON `${TABLE_NAME}` (`author_id`)"
}
],
"foreignKeys": [
{
"table": "news_resources",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"news_resource_id"
],
"referencedColumns": [
"id"
]
},
{
"table": "authors",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"author_id"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "news_resources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `episode_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `url` TEXT NOT NULL, `header_image_url` TEXT, `publish_date` INTEGER NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`episode_id`) REFERENCES `episodes`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "episodeId",
"columnName": "episode_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "headerImageUrl",
"columnName": "header_image_url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "publishDate",
"columnName": "publish_date",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": [
{
"table": "episodes",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"episode_id"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "news_resources_topics",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`news_resource_id` INTEGER NOT NULL, `topic_id` INTEGER NOT NULL, PRIMARY KEY(`news_resource_id`, `topic_id`), FOREIGN KEY(`news_resource_id`) REFERENCES `news_resources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`topic_id`) REFERENCES `topics`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "newsResourceId",
"columnName": "news_resource_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "topicId",
"columnName": "topic_id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"news_resource_id",
"topic_id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_news_resources_topics_news_resource_id",
"unique": false,
"columnNames": [
"news_resource_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_topics_news_resource_id` ON `${TABLE_NAME}` (`news_resource_id`)"
},
{
"name": "index_news_resources_topics_topic_id",
"unique": false,
"columnNames": [
"topic_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_topics_topic_id` ON `${TABLE_NAME}` (`topic_id`)"
}
],
"foreignKeys": [
{
"table": "news_resources",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"news_resource_id"
],
"referencedColumns": [
"id"
]
},
{
"table": "topics",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"topic_id"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "topics",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `longDescription` TEXT NOT NULL DEFAULT '', `url` TEXT NOT NULL DEFAULT '', `imageUrl` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shortDescription",
"columnName": "shortDescription",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "longDescription",
"columnName": "longDescription",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "imageUrl",
"columnName": "imageUrl",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_topics_name",
"unique": true,
"columnNames": [
"name"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_topics_name` ON `${TABLE_NAME}` (`name`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6bd08a2b063057f8fa1a1cfe31f7aca4')"
]
}
}

@ -44,12 +44,13 @@ import com.google.samples.apps.nowinandroid.core.database.util.NewsResourceTypeC
NewsResourceTopicCrossRef::class,
TopicEntity::class,
],
version = 5,
version = 6,
autoMigrations = [
AutoMigration(from = 1, to = 2),
AutoMigration(from = 2, to = 3, spec = DatabaseMigrations.Schema2to3::class),
AutoMigration(from = 3, to = 4),
AutoMigration(from = 4, to = 5),
AutoMigration(from = 5, to = 6),
],
exportSchema = true,
)

@ -18,7 +18,6 @@ package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import com.google.samples.apps.nowinandroid.core.model.data.Author
@ -28,9 +27,6 @@ import com.google.samples.apps.nowinandroid.core.model.data.Author
*/
@Entity(
tableName = "authors",
indices = [
Index(value = ["name"], unique = true)
],
)
data class AuthorEntity(
@PrimaryKey

@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
/**
* Cross reference for many to many relationship between [EpisodeEntity] and [AuthorEntity]
@ -39,7 +40,11 @@ import androidx.room.ForeignKey
childColumns = ["author_id"],
onDelete = ForeignKey.CASCADE
),
]
],
indices = [
Index(value = ["episode_id"]),
Index(value = ["author_id"]),
],
)
data class EpisodeAuthorCrossRef(
@ColumnInfo(name = "episode_id")

@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
/**
* Cross reference for many to many relationship between [NewsResourceEntity] and [AuthorEntity]
@ -39,7 +40,11 @@ import androidx.room.ForeignKey
childColumns = ["author_id"],
onDelete = ForeignKey.CASCADE
),
]
],
indices = [
Index(value = ["news_resource_id"]),
Index(value = ["author_id"]),
],
)
data class NewsResourceAuthorCrossRef(
@ColumnInfo(name = "news_resource_id")

@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
/**
* Cross reference for many to many relationship between [NewsResourceEntity] and [TopicEntity]
@ -39,7 +40,11 @@ import androidx.room.ForeignKey
childColumns = ["topic_id"],
onDelete = ForeignKey.CASCADE
),
]
],
indices = [
Index(value = ["news_resource_id"]),
Index(value = ["topic_id"]),
],
)
data class NewsResourceTopicCrossRef(
@ColumnInfo(name = "news_resource_id")

@ -14,20 +14,14 @@
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.sync.di
package com.google.samples.apps.nowinandroid.core.datastore
import com.google.samples.apps.nowinandroid.sync.LocalSyncRepository
import com.google.samples.apps.nowinandroid.sync.SyncRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface SyncModule {
@Binds
fun bindsSyncStatusRepository(
localSyncRepository: LocalSyncRepository
): SyncRepository
}
/**
* Class summarizing the local version of each model for sync
*/
data class ChangeListVersions(
val topicVersion: Int = -1,
val authorVersion: Int = -1,
val episodeVersion: Int = -1,
val newsResourceVersion: Int = -1,
)

@ -25,13 +25,6 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.retry
data class ChangeListVersions(
val topicVersion: Int = -1,
val authorVersion: Int = -1,
val episodeVersion: Int = -1,
val newsResourceVersion: Int = -1,
)
class NiaPreferences @Inject constructor(
private val userPreferences: DataStore<UserPreferences>
) {
@ -74,21 +67,6 @@ class NiaPreferences @Inject constructor(
}
.map { it.followedTopicIdsList.toSet() }
suspend fun hasRunFirstTimeSync() = userPreferences.data
.map { it.hasRunFirstTimeSync }.firstOrNull() ?: false
suspend fun markFirstTimeSyncDone() {
try {
userPreferences.updateData {
it.copy {
hasRunFirstTimeSync = true
}
}
} catch (ioException: IOException) {
Log.e("NiaPreferences", "Failed to update user preferences", ioException)
}
}
suspend fun getChangeListVersions() = userPreferences.data
.map {
ChangeListVersions(

@ -20,8 +20,8 @@ option java_package = "com.google.samples.apps.nowinandroid.core.datastore";
option java_multiple_files = true;
message UserPreferences {
reserved 2;
repeated int32 followed_topic_ids = 1;
bool has_run_first_time_sync = 2;
int32 topicChangeListVersion = 3;
int32 authorChangeListVersion = 4;
int32 episodeChangeListVersion = 5;

@ -41,7 +41,6 @@ class UserPreferencesSerializerTest {
val expectedUserPreferences = userPreferences {
followedTopicIds.add(0)
followedTopicIds.add(1)
hasRunFirstTimeSync = true
}
val outputStream = ByteArrayOutputStream()

@ -26,7 +26,7 @@ import kotlin.coroutines.cancellation.CancellationException
* Attempts [block], returning a successful [Result] if it succeeds, otherwise a [Result.Failure]
* taking care not to break structured concurrency
*/
suspend fun <T> suspendRunCatching(block: suspend () -> T): Result<T> = try {
private suspend fun <T> suspendRunCatching(block: suspend () -> T): Result<T> = try {
Result.success(block())
} catch (cancellationException: CancellationException) {
throw cancellationException

@ -1,49 +0,0 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.sync
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import javax.inject.Inject
/**
* Source of truth for status of sync.
*/
interface SyncRepository {
/**
* returns whether we should run a first time sync job
*/
suspend fun shouldRunFirstTimeSync(): Boolean
/**
* Notify the repository that first time sync has run successfully
*/
suspend fun notifyFirstTimeSyncRun()
}
/**
* Uses a [NiaPreferences] to manage sync status
*/
class LocalSyncRepository @Inject constructor(
private val preferences: NiaPreferences
) : SyncRepository {
override suspend fun shouldRunFirstTimeSync(): Boolean = !preferences.hasRunFirstTimeSync()
override suspend fun notifyFirstTimeSyncRun() {
preferences.markFirstTimeSyncDone()
}
}

@ -22,7 +22,6 @@ import androidx.startup.Initializer
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.WorkManager
import androidx.work.WorkManagerInitializer
import com.google.samples.apps.nowinandroid.sync.workers.FirstTimeSyncCheckWorker
import com.google.samples.apps.nowinandroid.sync.workers.SyncWorker
object Sync {
@ -39,19 +38,18 @@ object Sync {
private const val SyncWorkName = "SyncWorkName"
/**
* Registers sync to sync the data layer periodically [SyncInterval] on app startup.
* Registers work to sync the data layer periodically on app startup.
*/
class SyncInitializer : Initializer<Sync> {
override fun create(context: Context): Sync {
val workManager = WorkManager.getInstance(context)
workManager.enqueueUniquePeriodicWork(
SyncWorkName,
ExistingPeriodicWorkPolicy.KEEP,
SyncWorker.periodicSyncWork()
)
workManager.enqueue(FirstTimeSyncCheckWorker.firstTimeSyncCheckWork())
WorkManager.getInstance(context).apply {
enqueue(SyncWorker.startUpSyncWork())
enqueueUniquePeriodicWork(
SyncWorkName,
ExistingPeriodicWorkPolicy.KEEP,
SyncWorker.periodicSyncWork()
)
}
return Sync
}

@ -1,66 +0,0 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.sync.workers
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.samples.apps.nowinandroid.sync.SyncRepository
import com.google.samples.apps.nowinandroid.sync.initializers.SyncConstraints
import com.google.samples.apps.nowinandroid.sync.initializers.syncForegroundInfo
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
/**
* Checks if the [SyncWorker] has ever run. If it hasn't, it requests one time sync to sync as
* quickly as possible, otherwise it does nothing.
*/
@HiltWorker
class FirstTimeSyncCheckWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted workerParams: WorkerParameters,
private val syncRepository: SyncRepository,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun getForegroundInfo(): ForegroundInfo =
appContext.syncForegroundInfo()
override suspend fun doWork(): Result {
if (!syncRepository.shouldRunFirstTimeSync()) return Result.success()
WorkManager.getInstance(appContext)
.enqueue(SyncWorker.firstTimeSyncWork())
return Result.success()
}
companion object {
/**
* Work that runs to enqueue an immediate first time sync if the app has yet to sync at all
*/
fun firstTimeSyncCheckWork() = OneTimeWorkRequestBuilder<DelegatingWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setConstraints(SyncConstraints)
.setInputData(FirstTimeSyncCheckWorker::class.delegatedData())
.build()
}
}

@ -27,7 +27,6 @@ import androidx.work.WorkerParameters
import com.google.samples.apps.nowinandroid.core.domain.repository.AuthorsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.sync.SyncRepository
import com.google.samples.apps.nowinandroid.sync.initializers.SyncConstraints
import com.google.samples.apps.nowinandroid.sync.initializers.syncForegroundInfo
import dagger.assisted.Assisted
@ -45,7 +44,6 @@ import kotlinx.coroutines.coroutineScope
class SyncWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted workerParams: WorkerParameters,
private val syncRepository: SyncRepository,
private val topicRepository: TopicsRepository,
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository,
@ -62,14 +60,8 @@ class SyncWorker @AssistedInject constructor(
async { newsRepository.sync() },
).all { it }
when (syncedSuccessfully) {
// Sync ran successfully, notify the SyncRepository that sync has been run
true -> {
syncRepository.notifyFirstTimeSyncRun()
Result.success()
}
false -> Result.retry()
}
if (syncedSuccessfully) Result.success()
else Result.retry()
}
companion object {
@ -77,10 +69,9 @@ class SyncWorker @AssistedInject constructor(
private val SyncIntervalTimeUnit = TimeUnit.DAYS
/**
* Expedited one time work to sync data as quickly as possible because the app has
* either launched for the first time, or hasn't had the opportunity to sync at all
* Expedited one time work to sync data on app startup
*/
fun firstTimeSyncWork() = OneTimeWorkRequestBuilder<DelegatingWorker>()
fun startUpSyncWork() = OneTimeWorkRequestBuilder<DelegatingWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setConstraints(SyncConstraints)
.setInputData(SyncWorker::class.delegatedData())

Loading…
Cancel
Save