summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DownloadRegionActivity.kt
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DownloadRegionActivity.kt')
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DownloadRegionActivity.kt313
1 files changed, 313 insertions, 0 deletions
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DownloadRegionActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DownloadRegionActivity.kt
new file mode 100644
index 0000000000..7f90796f42
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DownloadRegionActivity.kt
@@ -0,0 +1,313 @@
+package com.mapbox.mapboxsdk.testapp.activity.offline
+
+import android.os.Bundle
+import android.os.Handler
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.widget.ArrayAdapter
+import android.widget.SeekBar
+import android.widget.TextView
+import android.widget.Toast
+import com.mapbox.mapboxsdk.constants.MapboxConstants
+import com.mapbox.mapboxsdk.geometry.LatLng
+import com.mapbox.mapboxsdk.geometry.LatLngBounds
+import com.mapbox.mapboxsdk.maps.Style
+import com.mapbox.mapboxsdk.offline.*
+import com.mapbox.mapboxsdk.testapp.R
+import kotlinx.android.synthetic.main.activity_region_download.*
+import timber.log.Timber
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+/**
+ * Example showcasing how to download an offline region headless, allows to test pausing and resuming the download.
+ */
+class DownloadRegionActivity : AppCompatActivity(), OfflineRegion.OfflineRegionObserver {
+
+ companion object {
+ const val STATUS_UPDATE_TIMEOUT_MS = 10_000L
+ }
+
+ private val handler: Handler = Handler()
+ private lateinit var offlineManager: OfflineManager
+ private var offlineRegion: OfflineRegion? = null
+ private var downloading = false
+ private var previousCompletedResourceCount: Long = 0
+ private var previousUpdateTimestamp: Long = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_region_download)
+
+ offlineManager = OfflineManager.getInstance(this)
+ offlineManager.setOfflineMapboxTileCountLimit(Long.MAX_VALUE)
+ initUi()
+
+ deleteOldOfflineRegions {
+ container.visibility = View.VISIBLE
+ fab.visibility = View.VISIBLE
+ }
+ }
+
+ override fun onDestroy() {
+ handler.removeCallbacksAndMessages(null)
+ offlineRegion?.setObserver(null)
+ super.onDestroy()
+ }
+
+ private fun createOfflineRegion() {
+ val latitudeNorth = editTextLatNorth.text.toString().toDouble()
+ val longitudeEast = editTextLonEast.text.toString().toDouble()
+ val latitudeSouth = editTextLatSouth.text.toString().toDouble()
+ val longitudeWest = editTextLonWest.text.toString().toDouble()
+ val styleUrl = spinnerStyleUrl.selectedItem as String
+ val maxZoom = seekbarMaxZoom.progress.toFloat()
+ val minZoom = seekbarMinZoom.progress.toFloat()
+
+ if (!validCoordinates(latitudeNorth, longitudeEast, latitudeSouth, longitudeWest)) {
+ Toast.makeText(this, "coordinates need to be in valid range", Toast.LENGTH_LONG).show()
+ return
+ }
+
+ // create offline definition from data
+ val definition = OfflineTilePyramidRegionDefinition(
+ styleUrl,
+ LatLngBounds.Builder()
+ .include(LatLng(latitudeNorth, longitudeEast))
+ .include(LatLng(latitudeSouth, longitudeWest))
+ .build(),
+ minZoom.toDouble(),
+ maxZoom.toDouble(),
+ resources.displayMetrics.density
+ )
+
+ logMessage("Creating offline region")
+ offlineManager.createOfflineRegion(definition, byteArrayOf(), object : OfflineManager.CreateOfflineRegionCallback {
+ override fun onCreate(region: OfflineRegion) {
+ logMessage("Region with id ${region.id} created")
+ offlineRegion = region
+ startDownload(region)
+ fab.visibility = View.VISIBLE
+ }
+
+ override fun onError(error: String) {
+ logMessage("Failed to create offline region: $error")
+ }
+ })
+ }
+
+ private fun startDownload(region: OfflineRegion) {
+ downloading = true
+ fab.setImageResource(R.drawable.ic_pause_black_24dp)
+ logMessage("Downloading...")
+
+ region.setObserver(this)
+ region.setDownloadState(OfflineRegion.STATE_ACTIVE)
+
+ previousUpdateTimestamp = System.currentTimeMillis()
+ handler.postDelayed(object : Runnable {
+ override fun run() {
+ if (System.currentTimeMillis() > previousUpdateTimestamp + STATUS_UPDATE_TIMEOUT_MS) {
+ logMessage("FAILURE! No progress in ${TimeUnit.MILLISECONDS.toSeconds(STATUS_UPDATE_TIMEOUT_MS)} seconds")
+ } else {
+ handler.postDelayed(this, 1000)
+ }
+ }
+ }, 1000)
+ }
+
+ private fun pauseDownload(region: OfflineRegion) {
+ downloading = false
+ fab.setImageResource(R.drawable.ic_play_arrow_black_24dp)
+ handler.removeCallbacksAndMessages(null)
+ region.setDownloadState(OfflineRegion.STATE_INACTIVE)
+ "Paused".let {
+ logMessage(it)
+ download_status.text = it
+ }
+ }
+
+ override fun onStatusChanged(status: OfflineRegionStatus) {
+ if (status.isComplete) {
+ val statusText = "Completed"
+ logMessage("SUCCESS! $statusText")
+ download_status.text = statusText
+ offlineRegion?.setObserver(null)
+ handler.removeCallbacksAndMessages(null)
+ } else {
+ val statusText = "Downloaded ${status.completedResourceCount}/${status.requiredResourceCount}"
+ Timber.d(statusText)
+ download_status.text = statusText
+
+ if (status.completedResourceCount > status.requiredResourceCount &&
+ previousCompletedResourceCount <= status.requiredResourceCount) {
+ logMessage("FAILURE! Completed > required")
+ }
+ }
+
+ previousCompletedResourceCount = status.completedResourceCount
+ previousUpdateTimestamp = System.currentTimeMillis()
+ }
+
+ override fun onError(error: OfflineRegionError) {
+ logMessage("Error: $error")
+ }
+
+ override fun mapboxTileCountLimitExceeded(limit: Long) {
+ logMessage("Error: tile count limit exceeded")
+ }
+
+ protected fun logMessage(message: String) {
+ Timber.d(message)
+ logView.append(message)
+ logView.append("\n")
+ }
+
+ fun deleteOldOfflineRegions(onCompleted: () -> Unit) {
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
+ override fun onList(offlineRegions: Array<out OfflineRegion>) {
+ val count = offlineRegions.size
+ var remainingCount = count
+ if (count > 0) {
+ logMessage("Deleting $count old region...")
+ offlineRegions.forEach {
+ it.delete(object : OfflineRegion.OfflineRegionDeleteCallback {
+ override fun onDelete() {
+ Timber.d("Deleted region with id ${it.id}")
+ onProcessed()
+ }
+
+ override fun onError(error: String) {
+ Timber.e("Failed to delete region: $error")
+ onProcessed()
+ }
+
+ private fun onProcessed() {
+ remainingCount--
+ if (remainingCount == 0) {
+ logMessage("Done deleting")
+ onCompleted()
+ }
+ }
+ })
+ }
+ } else {
+ onCompleted()
+ }
+ }
+
+ override fun onError(error: String) {
+ logMessage("Failed to list offline regions: $error")
+ onCompleted()
+ }
+ })
+ }
+
+ // ui
+
+ private val logView: TextView by lazy {
+ findViewById<TextView>(R.id.log_text)
+ }
+
+ private fun initUi() {
+ initEditTexts()
+ initSeekbars()
+ initSpinner()
+ initZoomLevelTextviews()
+ initSeekbarListeners()
+ initFab()
+ }
+
+ private fun initEditTexts() {
+ editTextLatNorth.setText("62.0")
+ editTextLonEast.setText("24.0")
+ editTextLatSouth.setText("60.0")
+ editTextLonWest.setText("22.5")
+ }
+
+ private fun initSeekbars() {
+ val maxZoom = MapboxConstants.MAXIMUM_ZOOM.toInt()
+ seekbarMinZoom.max = maxZoom
+ seekbarMinZoom.progress = 1
+ seekbarMaxZoom.max = maxZoom
+ seekbarMaxZoom.progress = 15
+ }
+
+ private fun initSpinner() {
+ val styles = ArrayList<String>()
+ styles.add(Style.MAPBOX_STREETS)
+ styles.add(Style.DARK)
+ styles.add(Style.LIGHT)
+ styles.add(Style.OUTDOORS)
+ val spinnerArrayAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, styles)
+ spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ spinnerStyleUrl.adapter = spinnerArrayAdapter
+ }
+
+ private fun initZoomLevelTextviews() {
+ textViewMaxText.text = String.format("Max zoom: %s", seekbarMaxZoom.progress)
+ textViewMinText.text = String.format("Min zoom: %s", seekbarMinZoom.progress)
+ }
+
+ private fun initFab() {
+ fab.setOnClickListener {
+ container.visibility = View.GONE
+ if (offlineRegion == null) {
+ fab.visibility = View.GONE
+ createOfflineRegion()
+ } else {
+ offlineRegion?.let {
+ if (downloading) {
+ pauseDownload(it)
+ } else {
+ startDownload(it)
+ }
+ }
+ }
+ }
+ }
+
+ private fun initSeekbarListeners() {
+ seekbarMaxZoom.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ textViewMaxText.text = String.format("Max zoom: %s", progress)
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
+
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
+
+ }
+ })
+
+ seekbarMinZoom.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ textViewMinText.text = String.format("Min zoom: %s", progress)
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
+
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
+
+ }
+ })
+ }
+
+ private fun validCoordinates(latitudeNorth: Double, longitudeEast: Double, latitudeSouth: Double,
+ longitudeWest: Double): Boolean {
+ if (latitudeNorth < -90 || latitudeNorth > 90) {
+ return false
+ } else if (longitudeEast < -180 || longitudeEast > 180) {
+ return false
+ } else if (latitudeSouth < -90 || latitudeSouth > 90) {
+ return false
+ } else if (longitudeWest < -180 || longitudeWest > 180) {
+ return false
+ }
+ return true
+ }
+}