Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use WorkManager for Upload/Download #5492

Merged
merged 9 commits into from
Feb 22, 2024
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ dependencies {
api("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")

// scheduling background jobs
implementation("androidx.work:work-runtime:2.9.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// finding in which country we are for country-specific logic
implementation("de.westnordost:countryboundaries:2.1")
Expand Down
23 changes: 15 additions & 8 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" android:minSdkVersion="34"/>
<!--
The app itself does not use foreground services but WorkManager.
WorkManager uses foreground services on Android older than Android 12 for expedited work, but
we expect that the manifest of the WorkManager library includes the uses-permission lines it
needs.

https://developer.android.com/develop/background-work/background-tasks/persistent/getting-started/define-work#backwards-compat

Foreground service types exists since Android 10.
-->

<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.screen.portrait" />
Expand Down Expand Up @@ -89,12 +96,12 @@
android:name="de.westnordost.streetcomplete.screens.user.UserActivity"
android:screenOrientation="portrait"
tools:ignore="LockedOrientationActivity" />
<!-- For WorkManager -->
<service
android:name="de.westnordost.streetcomplete.data.download.DownloadService"
android:foregroundServiceType="dataSync" />
<service
android:name="de.westnordost.streetcomplete.data.upload.UploadService"
android:foregroundServiceType="dataSync" />
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="@string/fileprovider_authority"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,79 +1,25 @@
package de.westnordost.streetcomplete.data.download

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import de.westnordost.streetcomplete.ApplicationConstants
import de.westnordost.streetcomplete.data.download.tiles.enclosingTilesRect
import androidx.work.ExistingWorkPolicy
import androidx.work.WorkManager
import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox

/** Controls downloading */
class DownloadController(
private val context: Context
) : DownloadProgressSource {

private var downloadServiceIsBound: Boolean = false
private var downloadService: DownloadService.Interface? = null
private val downloadServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
downloadService = service as DownloadService.Interface
downloadService?.setProgressListener(downloadProgressRelay)
}

override fun onServiceDisconnected(className: ComponentName) {
downloadService = null
}
}
private val downloadProgressRelay = DownloadProgressRelay()

/** @return true if a download triggered by the user is running */
override val isPriorityDownloadInProgress: Boolean get() =
downloadService?.isPriorityDownloadInProgress == true

/** @return true if a download is running */
override val isDownloadInProgress: Boolean get() =
downloadService?.isDownloadInProgress == true

var showNotification: Boolean
get() = downloadService?.showDownloadNotification == true
set(value) { downloadService?.showDownloadNotification = value }

init {
bindServices()
}
class DownloadController(private val context: Context) {

/** Download in at least the given bounding box asynchronously. The next-bigger rectangle
* in a (z16) tiles grid that encloses the given bounding box will be downloaded.
*
* @param bbox the minimum area to download
* @param isPriority whether this shall be a priority download (cancels previous downloads and
* puts itself in the front)
* @param isUserInitiated whether this shall be a priority download (cancels previous downloads
* and puts itself in the front)
*/
fun download(bbox: BoundingBox, isPriority: Boolean = false) {
if (downloadService == null) return

val tilesRect = bbox.enclosingTilesRect(ApplicationConstants.DOWNLOAD_TILE_ZOOM)
context.startService(DownloadService.createIntent(context, tilesRect, isPriority))
}

private fun bindServices() {
downloadServiceIsBound = context.bindService(
Intent(context, DownloadService::class.java),
downloadServiceConnection, Context.BIND_AUTO_CREATE
fun download(bbox: BoundingBox, isUserInitiated: Boolean = false) {
WorkManager.getInstance(context).enqueueUniqueWork(
Downloader.TAG,
if (isUserInitiated) ExistingWorkPolicy.REPLACE else ExistingWorkPolicy.KEEP,
DownloadWorker.createWorkRequest(bbox, isUserInitiated)
)
}

private fun unbindServices() {
if (downloadServiceIsBound) context.unbindService(downloadServiceConnection)
downloadServiceIsBound = false
}

override fun addDownloadProgressListener(listener: DownloadProgressListener) {
downloadProgressRelay.addListener(listener)
}
override fun removeDownloadProgressListener(listener: DownloadProgressListener) {
downloadProgressRelay.removeListener(listener)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import de.westnordost.streetcomplete.data.download.strategy.WifiAutoDownloadStra
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesController
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesDao
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesSource
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.workmanager.dsl.worker
import org.koin.core.qualifier.named
import org.koin.dsl.module

Expand All @@ -15,9 +17,11 @@ val downloadModule = module {

single { Downloader(get(), get(), get(), get(), get(named("SerializeSync"))) }

single<DownloadProgressSource> { get<DownloadController>() }
single<DownloadProgressSource> { get<Downloader>() }
single { DownloadController(get()) }

single<DownloadedTilesSource> { get<DownloadedTilesController>() }
single { DownloadedTilesController(get()) }

worker { DownloadWorker(get(), androidContext(), get()) }
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package de.westnordost.streetcomplete.data.download

interface DownloadProgressSource {
val isPriorityDownloadInProgress: Boolean
interface Listener {
fun onStarted() {}
fun onError(e: Exception) {}
fun onFinished() {}
fun onSuccess() {}
}

/** @return true if a download triggered by the user is running */
val isUserInitiatedDownloadInProgress: Boolean
/** @return true if a download is running */
val isDownloadInProgress: Boolean

fun addDownloadProgressListener(listener: DownloadProgressListener)
fun removeDownloadProgressListener(listener: DownloadProgressListener)
fun addListener(listener: Listener)
fun removeListener(listener: Listener)
}

This file was deleted.

Loading