Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android-offline-maps/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
61 changes: 61 additions & 0 deletions android-offline-maps/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}

android {
namespace = "com.example.mapbox_offline"
compileSdk = 36

defaultConfig {
applicationId = "com.example.mapbox_offline"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
}

dependencies {

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
implementation("com.mapbox.maps:android-ndk27:11.15.3")
implementation("com.mapbox.extension:maps-compose-ndk27:11.15.3")
Comment on lines +59 to +60

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's move these to libs.versions.toml

}
21 changes: 21 additions & 0 deletions android-offline-maps/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.mapbox_offline

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.mapbox_offline", appContext.packageName)
}
}
28 changes: 28 additions & 0 deletions android-offline-maps/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Mapboxoffline"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Mapboxoffline">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.example.mapbox_offline

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.mapbox.geojson.Point
import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState
import com.example.mapbox_offline.OfflineRegion

public class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Ensure Mapbox standard style pack is present (no-op if already downloaded).
OfflineRegionManager.ensureStylePackDownloaded(this)
setContent {
var showingDownload by remember { mutableStateOf(false) }

// Ensure system back button will dismiss the modal when open
if (showingDownload) {
BackHandler { showingDownload = false }
}

Box(Modifier.fillMaxSize()) {
MapboxMap(
Modifier.fillMaxSize(),
mapViewportState = rememberMapViewportState {
setCameraOptions {
zoom(2.0)
center(Point.fromLngLat(-98.0, 39.5))
pitch(0.0)
bearing(0.0)
}
},
)

// Download button overlay (top-right)
Box(
modifier = Modifier
.fillMaxSize()
// moved down by increasing top padding
.padding(start = 12.dp, end = 12.dp, top = 72.dp, bottom = 12.dp),
contentAlignment = Alignment.TopEnd
) {
Button(onClick = { showingDownload = true }) {
Icon(Icons.Default.KeyboardArrowDown, contentDescription = null)
Spacer(Modifier.width(8.dp))
Text("Manage Offline Regions")
}
}

// Present the TileRegionDownloadScreen as a full-screen overlay that completely covers the map
if (showingDownload) {
Surface(
modifier = Modifier
.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(modifier = Modifier.fillMaxSize()) {
// pass onDone so the top-left arrow dismisses the modal
TileRegionDownloadScreen(onDone = { showingDownload = false })
}
}
}

}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.mapbox_offline

import com.mapbox.geojson.Point
import com.mapbox.geojson.Polygon
import com.mapbox.maps.CoordinateBounds

data class OfflineRegion(
val id: String,
val name: String,
val bounds: CoordinateBounds
) {
// convert CoordinateBounds to Polygon (closed rectangular ring)
val polygon: Polygon
get() {
val sw = bounds.southwest
val ne = bounds.northeast
val coords = listOf(
Point.fromLngLat(sw.longitude(), sw.latitude()),
Point.fromLngLat(ne.longitude(), sw.latitude()),
Point.fromLngLat(ne.longitude(), ne.latitude()),
Point.fromLngLat(sw.longitude(), ne.latitude()),
Point.fromLngLat(sw.longitude(), sw.latitude())
)
return Polygon.fromLngLats(listOf(coords))
}
}
Loading