diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 01f32b540..18fbf33cc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -14,6 +14,7 @@
android:roundIcon="@drawable/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
-
+
+
+
+
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) {
+ 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(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()
+ 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
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pause_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pause_black_24dp.xml
new file mode 100644
index 000000000..bb28a6c41
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pause_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_region_download.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_region_download.xml
new file mode 100644
index 000000000..4242da9fb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_region_download.xml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
index cc2577fb5..be8dae57a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -18,6 +18,7 @@
Map Padding example
Debug Mode
Offline Map example
+ Download an offline region.
Update metadata example
Delete region example
Change resources cache path example
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
index e5f738191..027198c71 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
@@ -83,4 +83,5 @@
Nested ViewPager
Performance Measurement
Physical Unit Circle
+ Download region
\ No newline at end of file
diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json
index a6070edcc..f6156eb0e 100644
--- a/platform/android/scripts/exclude-activity-gen.json
+++ b/platform/android/scripts/exclude-activity-gen.json
@@ -50,5 +50,6 @@
"EspressoTestActivity",
"FragmentBackStackActivity",
"ChildFragmentMapInDialogActivity",
- "PerformanceMeasurementActivity"
+ "PerformanceMeasurementActivity",
+ "DownloadRegionActivity"
]