Skip to content
Merged
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
18 changes: 18 additions & 0 deletions .github/workflows/pull_requests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: PR Checks
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2.4.0
-
name: Configure Java
uses: actions/setup-java@v4.0.0
with:
java-version: 17
distribution: oracle
-
name: Tests
run: ./gradlew android-application:testStubDebugUnitTest android-extensions:testDebugUnitTest aprs-android:testDebugUnitTest maps:testDebugUnitTest
16 changes: 13 additions & 3 deletions android-application/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ android {
minSdk = 21
targetSdk = 34
multiDexEnabled = true
buildConfigField("String", "MAPBOX_ACCESS_TOKEN", optionalStringProperty("mapboxPublic").buildQuote())
buildConfigField("boolean", "USE_GOOGLE_SERVICES", useGoogleServices.toString())
buildConfigField("String", "COMMIT", optionalStringProperty("commit").buildQuote())
versionCode = intProperty("versionCode", 1)
Expand Down Expand Up @@ -63,6 +62,16 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}
flavorDimensions += "function"
productFlavors {
create("functional") {
isDefault = true
dimension = "function"
}
create("stub") {
dimension = "function"
}
}
compileOptions {
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
Expand Down Expand Up @@ -108,8 +117,6 @@ dependencies {

implementation(libs.google.material.core)

implementation(libs.mapbox.android.sdk)

implementation(libs.bundles.dagger.libraries)
kapt(libs.bundles.dagger.kapt)

Expand All @@ -127,6 +134,9 @@ dependencies {
implementation(libs.firebase.config)
implementation(libs.firebase.analytics)

implementation(projects.maps)
"functionalImplementation"(projects.mapbox)

testImplementation(libs.junit)
testImplementation(libs.coroutines.test)
testImplementation(libs.kotlin.test.core)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.inkapplications.ack.android.maps

import com.inkapplications.ack.android.mapbox.MapboxMapRenderer

val MapsImplementation = MapboxMapRenderer
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package com.inkapplications.ack.android.capture

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.view.View
import androidx.activity.compose.setContent
import androidx.compose.runtime.collectAsState
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.inkapplications.ack.android.R
import com.inkapplications.ack.android.capture.insights.InsightsController
import com.inkapplications.ack.android.capture.messages.conversation.startConversationActivity
import com.inkapplications.ack.android.capture.messages.create.CreateConversationActivity
Expand All @@ -21,10 +16,10 @@ import com.inkapplications.ack.android.connection.DriverSelection
import com.inkapplications.ack.android.log.LogItemViewState
import com.inkapplications.ack.android.log.details.startLogInspectActivity
import com.inkapplications.ack.android.log.index.LogIndexController
import com.inkapplications.ack.android.map.MapController
import com.inkapplications.ack.android.map.MapEvents
import com.inkapplications.ack.android.map.MapViewState
import com.inkapplications.ack.android.map.mapbox.createController
import com.inkapplications.ack.android.maps.CameraPositionDefaults
import com.inkapplications.ack.android.maps.MapViewModel
import com.inkapplications.ack.android.settings.SettingsActivity
import com.inkapplications.ack.android.station.startStationActivity
import com.inkapplications.ack.android.tnc.startConnectTncActivity
Expand All @@ -34,16 +29,9 @@ import com.inkapplications.ack.structures.station.Callsign
import com.inkapplications.android.PermissionGate
import com.inkapplications.android.extensions.ExtendedActivity
import com.inkapplications.android.startActivity
import com.inkapplications.coroutines.collectOn
import com.mapbox.maps.MapView
import dagger.hilt.android.AndroidEntryPoint
import kimchi.Kimchi
import kimchi.analytics.intProperty
import kimchi.analytics.stringProperty
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -62,17 +50,19 @@ class CaptureActivity: ExtendedActivity(), CaptureNavController, LogIndexControl
@Inject
lateinit var captureEvents: CaptureEvents

private var mapView: MapView? = null
private var mapScope: CoroutineScope = MainScope()
private val mapViewState = MutableStateFlow(MapViewState())
private val permissionGate = PermissionGate(this)
private val backgroundCaptureServiceIntent by lazy { Intent(this, BackgroundCaptureService::class.java) }

override fun onCreate() {
super.onCreate()

setContent {
val mapState = mapViewState.collectAsState()
val mapState = mapEvents.viewState.collectAsState(MapViewState(
mapViewModel = MapViewModel(
cameraPosition = CameraPositionDefaults.unknownLocation,
markers = emptyList(),
)
))
val messagesScreenController = object: MessagesScreenController {
override fun onCreateMessageClick() {
startActivity(CreateConversationActivity::class)
Expand All @@ -87,55 +77,21 @@ class CaptureActivity: ExtendedActivity(), CaptureNavController, LogIndexControl
logIndexController = this,
messagesScreenController = messagesScreenController,
insightsController = this,
mapFactory = ::createMapView,
controller = this,
)
}
}

private fun createMapView(context: Context): View {
return if(mapView != null) mapView!! else MapView(context).also { mapView ->
this.mapView = mapView

mapView.createController(this, ::onMapLoaded, ::onMapItemSelected)

return mapView
}
}

private fun onMapItemSelected(id: CaptureId?) {
Kimchi.trackEvent("map_item_select")
mapEvents.selectedItemId.value = id
}

private fun onMapLoaded(map: MapController) {
mapScope.cancel()
mapScope = MainScope()

map.setBottomPadding(resources.getDimension(R.dimen.mapbox_logo_padding_bottom))

when(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
PackageManager.PERMISSION_GRANTED -> map.zoomTo(mapEvents.initialState)
}

mapEvents.viewState.collectOn(mapScope) { state ->
Kimchi.trackEvent("map_markers", listOf(intProperty("quantity", state.markers.size)))
mapViewState.emit(state)
map.showMarkers(state.markers)

if (state.trackPosition) {
map.enablePositionTracking()
} else {
map.disablePositionTracking()
}
}
}

override fun onLogMapItemClick(log: LogItemViewState) {
Kimchi.trackEvent("map_log_click")
startStationActivity(log.source)
}

override fun onMapItemClick(captureId: CaptureId?) {
Kimchi.trackEvent("map_item_select")
mapEvents.selectedItemId.value = captureId
}

override fun onLogListItemClick(item: LogItemViewState) {
Kimchi.trackEvent("log_item_click")
startLogInspectActivity(item.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.inkapplications.ack.android.capture

import com.inkapplications.ack.android.connection.DriverSelection
import com.inkapplications.ack.android.log.LogItemViewState
import com.inkapplications.ack.data.CaptureId

/**
* Capture navigation and control panel options.
Expand Down Expand Up @@ -53,4 +54,9 @@ interface CaptureNavController {
* Invoked when the user selects a new radio driver.
*/
fun onDriverSelected(selection: DriverSelection)

/**
* Invoked when the user clicks an item on the map
*/
fun onMapItemClick(captureId: CaptureId?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

package com.inkapplications.ack.android.capture

import android.content.Context
import android.view.View
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
Expand Down Expand Up @@ -49,7 +47,6 @@ fun CaptureScreen(
mapState: State<MapViewState>,
logIndexController: LogIndexController,
messagesScreenController: MessagesScreenController,
mapFactory: (Context) -> View,
controller: CaptureNavController,
insightsController: InsightsController,
viewModel: CaptureViewModel = hiltViewModel(),
Expand All @@ -75,7 +72,6 @@ fun CaptureScreen(
mapState = mapState,
logIndexController = logIndexController,
messagesScreenController = messagesScreenController,
mapFactory = mapFactory,
captureController = controller,
insightsController = insightsController,
)
Expand Down Expand Up @@ -382,7 +378,6 @@ private fun CaptureNavHost(
mapState: State<MapViewState>,
logIndexController: LogIndexController,
messagesScreenController: MessagesScreenController,
mapFactory: (Context) -> View,
captureController: CaptureNavController,
insightsController: InsightsController,
) {
Expand All @@ -394,7 +389,7 @@ private fun CaptureNavHost(
composable("map") {
MapScreen(
state = mapState.value,
mapFactory = mapFactory,
onMapItemClick = captureController::onMapItemClick,
onLogItemClick = captureController::onLogMapItemClick,
onEnableLocation = captureController::onLocationEnableClick,
onDisableLocation = captureController::onLocationDisableClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package com.inkapplications.ack.android.log
import com.inkapplications.ack.android.locale.LocaleSettings
import com.inkapplications.ack.android.log.details.LogDetailData
import com.inkapplications.ack.android.log.index.LogIndexData
import com.inkapplications.ack.android.map.*
import com.inkapplications.ack.android.map.MarkerViewStateFactory
import com.inkapplications.ack.android.maps.CameraPositionDefaults
import com.inkapplications.ack.android.maps.MapCameraPosition
import com.inkapplications.ack.android.maps.MapViewModel
import com.inkapplications.ack.android.maps.ZoomLevels
import com.inkapplications.ack.android.settings.SettingsReadAccess
import com.inkapplications.ack.android.settings.observeBoolean
import com.inkapplications.ack.android.station.StationSettings
Expand Down Expand Up @@ -51,13 +55,13 @@ class LogEvents @Inject constructor(
}
}

fun mapState(id: CaptureId): Flow<MapState> {
fun mapState(id: CaptureId): Flow<MapViewModel> {
return packetStorage.findById(id)
.filterNotNull()
.map { packet ->
MapState(
MapViewModel(
markers = markerViewStateFactory.create(packet)?.let { listOf(it) }.orEmpty(),
mapCameraPosition = (packet.parsed.data as? Mapable)?.coordinates
cameraPosition = (packet.parsed.data as? Mapable)?.coordinates
?.let { MapCameraPosition(it, ZoomLevels.ROADS) }
?: CameraPositionDefaults.unknownLocation,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
package com.inkapplications.ack.android.log.details

import android.app.Activity
import android.content.Context
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.lifecycle.lifecycleScope
import com.inkapplications.ack.android.log.LogEvents
import com.inkapplications.ack.android.map.MapController
import com.inkapplications.ack.android.map.mapbox.createController
import com.inkapplications.ack.android.station.startStationActivity
import com.inkapplications.ack.android.trackNavigation
import com.inkapplications.ack.android.ui.theme.AckScreen
import com.inkapplications.ack.data.CaptureId
import com.inkapplications.ack.structures.station.Callsign
import com.inkapplications.android.extensions.ExtendedActivity
import com.inkapplications.android.startActivity
import com.mapbox.maps.MapView
import dagger.hilt.android.AndroidEntryPoint
import kimchi.Kimchi
import kotlinx.coroutines.flow.collectLatest
import javax.inject.Inject

const val EXTRA_LOG_ID = "aprs.station.extra.id"

Expand All @@ -28,11 +20,6 @@ const val EXTRA_LOG_ID = "aprs.station.extra.id"
*/
@AndroidEntryPoint
class LogDetailsActivity: ExtendedActivity(), LogDetailsController {
@Inject
lateinit var logEvents: LogEvents

private var mapView: MapView? = null

private val id get() = CaptureId(intent.getLongExtra(EXTRA_LOG_ID, -1))

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -43,29 +30,12 @@ class LogDetailsActivity: ExtendedActivity(), LogDetailsController {
AckScreen {
LogDetailsScreen(
controller = this,
mapViewFactory = ::createMapView,
)
}
}
}

private fun createMapView(context: Context) = mapView ?: MapView(context).also { mapView ->
this.mapView = mapView

mapView.createController(this, ::onMapLoaded, ::onMapItemClicked)
}

private fun onMapLoaded(map: MapController) {
Kimchi.trace("Map Loaded")
lifecycleScope.launchWhenCreated {
logEvents.mapState(id).collectLatest { state ->
map.setCamera(state.mapCameraPosition)
map.showMarkers(state.markers)
}
}
}

private fun onMapItemClicked(id: CaptureId?) {
override fun onMapItemClicked(id: CaptureId?) {
Kimchi.trackEvent("log_details_map_item_click")
Kimchi.debug("Map Item Clicked: No-Op")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.inkapplications.ack.android.log.details

import com.inkapplications.ack.data.CaptureId
import com.inkapplications.ack.structures.station.Callsign

/**
Expand All @@ -15,4 +16,11 @@ interface LogDetailsController {
* Invoked when the user clicks on the station details button.
*/
fun onViewStationDetails(station: Callsign)

/**
* Invoked when the user clicks on a map marker.
*
* @param id The id of the map marker that was clicked.
*/
fun onMapItemClicked(id: CaptureId?)
}
Loading