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

Add button to toggle location tracking #88

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion Android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ android {
minSdk 21

def versionNameDate = new Date().format('yyyy.MM.dd:HH.mm')
versionCode 28
versionCode 29
versionName "$versionNameDate"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ import android.os.Process
import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.core.app.ActivityCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.FragmentActivity
Expand Down Expand Up @@ -57,6 +55,10 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {
var locationEnabled = false
private set

private var manuallyDisabled = false
private var isTrackingLocation = false
private var locationTrackingButton: MapOptionItem? = null

private lateinit var optionsView: DialogPlus

private val serviceConnection = object : ServiceConnection {
Expand All @@ -66,12 +68,14 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {
skierLocationService = binder.getService()
bound = true
skierLocationService!!.setCallbacks(this@MapsActivity)
setIsTracking(true)
}

override fun onServiceDisconnected(name: ComponentName?) {
skierLocationService!!.setCallbacks(null)
unbindService(this)
skierLocationService = null
setIsTracking(false)
bound = false
}
}
Expand Down Expand Up @@ -140,6 +144,7 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {
if (bound) {
skierLocationService!!.setCallbacks(null)
unbindService(serviceConnection)
skierLocationService = null
bound = false
}

Expand All @@ -149,7 +154,8 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {
override fun onResume() {
super.onResume()

if (isMapSetup && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
if (isMapSetup && !manuallyDisabled && !isTrackingLocation &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
launchLocationService()
}
Expand All @@ -176,6 +182,7 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {

val serviceIntent = Intent(this, SkierLocationService::class.java)
serviceIntent.action = SkierLocationService.START_TRACKING_INTENT

// Check if the service has already been started and is running...
val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
Expand All @@ -194,6 +201,8 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {

bindService(serviceIntent, serviceConnection, Context.BIND_NOT_FOREGROUND)
startService(serviceIntent)
} else {
Log.w("launchLocationService", "GPS not enabled")
}
}

Expand Down Expand Up @@ -231,6 +240,19 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {
}
}

override fun setIsTracking(isTracking: Boolean) {
isTrackingLocation = isTracking

val button = locationTrackingButton ?: return
if (button.itemEnabled != isTracking) {
button.toggleOptionVisibility()
}
}

override fun setManuallyDisabled(manuallyDisabled: Boolean) {
this.manuallyDisabled = manuallyDisabled
}

companion object {
const val permissionValue = 29500
}
Expand Down Expand Up @@ -294,26 +316,49 @@ class MapsActivity : FragmentActivity(), SkierLocationService.ServiceCallbacks {

private inner class OptionsDialog : MapOptionsDialog(layoutInflater, R.layout.main_options, map) {

private var showTerrainParkButton: MapOptionItem? = null

private var launchActivitySummaryImage: MapOptionItem? = null

override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view = super.getView(position, convertView, parent)

if (launchActivitySummaryImage != null) {
return view
if (showTerrainParkButton == null) {
val terrainParkButton: MapOptionItem = view.findViewById(R.id.show_terrain_parks)
terrainParkButton.setOnClickListener {
terrainParkButton.toggleOptionVisibility()
}
showTerrainParkButton = terrainParkButton
}

val activitySummaryImage: MapOptionItem? = view.findViewById(R.id.launch_activity_summary)
if (activitySummaryImage == null) {
Log.w("getView", "Unable to find activity summary launcher")
return view
if (locationTrackingButton == null) {
val toggleLocationTracking: MapOptionItem = view.findViewById(R.id.toggle_location_tracking)
toggleLocationTracking.setOnClickListener {
if (isTrackingLocation) {
manuallyDisabled = true
skierLocationService?.stopService() ?: Log.w("onClick",
"Unable to stop location tracking")
} else {
manuallyDisabled = false
launchLocationService()
}
}

if (toggleLocationTracking.itemEnabled != isTrackingLocation) {
toggleLocationTracking.toggleOptionVisibility()
}

locationTrackingButton = toggleLocationTracking
}

activitySummaryImage.setOnClickListener {
optionsView.dismiss()
startActivity(Intent(this@MapsActivity, ActivitySummary::class.java))
if (launchActivitySummaryImage == null) {
val activitySummaryImage: MapOptionItem = view.findViewById(R.id.launch_activity_summary)
activitySummaryImage.setOnClickListener {
optionsView.dismiss()
startActivity(Intent(this@MapsActivity, ActivitySummary::class.java))
}
launchActivitySummaryImage = activitySummaryImage
}
launchActivitySummaryImage = activitySummaryImage

return view
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,47 @@ class SkierLocationService : Service(), LocationListener {
private lateinit var skiingDate: SkiingDate

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.v("SkierLocationService", "onStartCommand called!")
val tag = "SkierLocationService"
Log.v(tag, "onStartCommand called!")
super.onStartCommand(intent, flags, startId)

val notification: Notification = createPersistentNotification("", null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(TRACKING_SERVICE_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
} else {
startForeground(TRACKING_SERVICE_ID, notification)
if (intent == null) {
Log.w(tag, "SkierLocationService started without intent")
return START_NOT_STICKY
}

val action = intent.action
if (action == null) {
Log.w(tag, "SkierLocationService intent missing action")
return START_NOT_STICKY
}
Log.v(tag, action)

when (action) {
START_TRACKING_INTENT -> {
Log.d(tag, "Starting foreground service")

val notification: Notification = createPersistentNotification("", null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(TRACKING_SERVICE_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
} else {
startForeground(TRACKING_SERVICE_ID, notification)
}
serviceCallbacks?.setIsTracking(true)
}
STOP_TRACKING_INTENT -> {
Log.d(tag, "Stopping foreground service")

serviceCallbacks?.setManuallyDisabled(true) ?: Log.w(
tag,
"Unable to set manually disabled"
)

stopService()
}
else -> Log.w(tag, "Unknown intent action: $action")
}

Log.d("SkierLocationService", "Started foreground service")
return START_NOT_STICKY
}

Expand Down Expand Up @@ -129,6 +159,7 @@ class SkierLocationService : Service(), LocationListener {
Log.v("SkierLocationService", "onDestroy has been called!")
super.onDestroy()

serviceCallbacks?.setIsTracking(false)
locationManager.removeUpdates(this)
notificationManager.cancel(TRACKING_SERVICE_ID)

Expand All @@ -152,11 +183,10 @@ class SkierLocationService : Service(), LocationListener {

// If we are not on the mountain stop the tracking.
if (!localServiceCallback.isInBounds(location)) {
Toast.makeText(this, R.string.out_of_bounds,
Toast.LENGTH_LONG).show()
Toast.makeText(this, R.string.out_of_bounds, Toast.LENGTH_LONG).show()
notificationManager.cancel(TRACKING_SERVICE_ID)
Log.d("SkierLocationService", "Stopping location tracking service")
stopSelf()
stopService()
return
}

Expand Down Expand Up @@ -236,9 +266,15 @@ class SkierLocationService : Service(), LocationListener {

private fun createPersistentNotification(title: String, iconBitmap: Bitmap?): Notification {
val pendingIntent: PendingIntent = createPendingIntent(MapsActivity::class, skiingDate.id)
val stopIntent = Intent(this, SkierLocationService::class.java)
stopIntent.action = STOP_TRACKING_INTENT

val builder: NotificationCompat.Builder = getNotificationBuilder(TRACKING_SERVICE_CHANNEL_ID,
false, R.string.tracking_notice, pendingIntent)
builder.setContentText(title)
.setContentText(title)
.addAction(0, "Stop Tracking", PendingIntent.getService(this,
0, stopIntent,
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE))

if (iconBitmap != null) {
builder.setLargeIcon(iconBitmap)
Expand All @@ -253,10 +289,20 @@ class SkierLocationService : Service(), LocationListener {
return NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.icon_fg)
.setShowWhen(showTime)
.setContentTitle(this.getString(titleText))
.setContentTitle(getString(titleText))
.setContentIntent(pendingIntent)
}

fun stopService() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE)
} else {
@Suppress("DEPRECATION")
stopForeground(true)
}
stopSelf()
}

override fun onBind(intent: Intent?): IBinder? {
return binder
}
Expand All @@ -280,6 +326,10 @@ class SkierLocationService : Service(), LocationListener {

const val ACTIVITY_SUMMARY_LAUNCH_DATE = "activitySummaryLaunchDate"

const val STOP_TRACKING_INTENT = "com.mtspokane.skiapp.SkierLocationService.Stop"

const val START_TRACKING_INTENT = "com.mtspokane.skiapp.SkierLocationService.Start"

/**
* @author https://studiofreya.com/2018/08/15/android-notification-large-icon-from-vector-xml/
*/
Expand Down Expand Up @@ -308,5 +358,9 @@ class SkierLocationService : Service(), LocationListener {
fun getInLocation(location: Location): MapMarker?

fun updateMapMarker(locationString: String)

fun setIsTracking(isTracking: Boolean)

fun setManuallyDisabled(manuallyDisabled: Boolean)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class MapOptionItem: LinearLayout {
private val enabledText: CharSequence
private val disabledText: CharSequence

private var itemEnabled: Boolean
var itemEnabled: Boolean
private set

constructor(context: Context): this(context, null)

Expand Down
12 changes: 12 additions & 0 deletions Android/app/src/main/res/drawable/ic_pause.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="32dp"
android:viewportWidth="24"
android:viewportHeight="32">
<path
android:pathData="M0,0h8v32h-8z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M16,0h8v32h-8z"
android:fillColor="#FFFFFF"/>
</vector>
9 changes: 9 additions & 0 deletions Android/app/src/main/res/drawable/ic_play.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="32dp"
android:viewportWidth="24"
android:viewportHeight="32">
<path
android:pathData="M0,0l24,16l-24,16z"
android:fillColor="#FFFFFF"/>
</vector>
21 changes: 21 additions & 0 deletions Android/app/src/main/res/drawable/terrain_park.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="200"
android:viewportHeight="200">
<path
android:pathData="M7.97,100a43.03,43.03 0,1 0,86.07 0a43.03,43.03 0,1 0,-86.07 0z"
android:strokeWidth="13.93511811"
android:fillColor="#f6a037"
android:strokeColor="#f6a037"/>
<path
android:pathData="M105.97,100a43.03,43.03 0,1 0,86.07 0a43.03,43.03 0,1 0,-86.07 0z"
android:strokeWidth="13.9347"
android:fillColor="#f6a037"
android:strokeColor="#f6a037"/>
<path
android:pathData="M55.08,55.08h89.83v89.83h-89.83z"
android:strokeWidth="10.1679"
android:fillColor="#f6a037"
android:strokeColor="#f6a037"/>
</vector>
26 changes: 26 additions & 0 deletions Android/app/src/main/res/drawable/terrain_park_disabled.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="200"
android:viewportHeight="200">
<path
android:pathData="M7.97,100a43.03,43.03 0,1 0,86.07 0a43.03,43.03 0,1 0,-86.07 0z"
android:strokeWidth="13.9347"
android:fillColor="#999999"
android:strokeColor="#999999"/>
<path
android:pathData="M105.97,100a43.03,43.03 0,1 0,86.07 0a43.03,43.03 0,1 0,-86.07 0z"
android:strokeWidth="13.9347"
android:fillColor="#999999"
android:strokeColor="#999999"/>
<path
android:pathData="M55.08,55.08h89.83v89.83h-89.83z"
android:strokeWidth="10.1679"
android:fillColor="#999999"
android:strokeColor="#999999"/>
<path
android:pathData="M-1.61,-1.7 L202.25,200.99"
android:strokeWidth="11.6504"
android:fillColor="#ff0000"
android:strokeColor="#ff0000"/>
</vector>
Loading