Created
April 7, 2025 15:00
-
-
Save XerxesZorgon/a8a728ce10bef894f3f1c53bdc532c36 to your computer and use it in GitHub Desktop.
Buckaroo Time app for Android. Calculates solar time based on phone's GPS location and UTC time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.example.buckarootime | |
| import android.Manifest | |
| import android.content.pm.PackageManager | |
| import android.os.Bundle | |
| import android.os.Looper | |
| import androidx.activity.ComponentActivity | |
| import androidx.activity.compose.setContent | |
| import androidx.activity.result.contract.ActivityResultContracts | |
| import androidx.compose.foundation.layout.* | |
| import androidx.compose.material3.* | |
| import androidx.compose.runtime.* | |
| import androidx.compose.ui.Modifier | |
| import androidx.core.app.ActivityCompat | |
| import com.example.buckarootime.ui.theme.BuckarooTimeTheme | |
| import com.google.android.gms.location.* | |
| import java.time.ZoneOffset | |
| import java.time.ZonedDateTime | |
| import kotlin.math.* | |
| import androidx.compose.ui.unit.dp | |
| import java.util.Locale | |
| class MainActivity : ComponentActivity() { | |
| private lateinit var fusedLocationClient: FusedLocationProviderClient | |
| private lateinit var locationRequest: LocationRequest | |
| private lateinit var locationCallback: LocationCallback | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) | |
| val solarTimeState = mutableStateOf("Calculating...") | |
| locationRequest = LocationRequest.Builder( | |
| Priority.PRIORITY_HIGH_ACCURACY, 60000L | |
| ).apply { | |
| setMinUpdateIntervalMillis(5000L) | |
| }.build() | |
| locationCallback = object : LocationCallback() { | |
| override fun onLocationResult(locationResult: LocationResult) { | |
| for (location in locationResult.locations) { | |
| val longitude = location.longitude | |
| val solarTime = calculateSolarTime(longitude) | |
| solarTimeState.value = "Solar Time: $solarTime\nLongitude: $longitude" | |
| } | |
| } | |
| } | |
| requestPermission { | |
| if (it && ActivityCompat.checkSelfPermission( | |
| this, | |
| Manifest.permission.ACCESS_FINE_LOCATION | |
| ) == PackageManager.PERMISSION_GRANTED | |
| ) { | |
| try { | |
| fusedLocationClient.requestLocationUpdates( | |
| locationRequest, | |
| locationCallback, | |
| Looper.getMainLooper() | |
| ) | |
| } catch (e: SecurityException) { | |
| solarTimeState.value = "SecurityException: ${e.message}" | |
| } | |
| } else { | |
| solarTimeState.value = "Permission Denied" | |
| } | |
| } | |
| setContent { | |
| BuckarooTimeTheme { | |
| Scaffold(modifier = Modifier.fillMaxSize()) { padding -> | |
| Column( | |
| modifier = Modifier | |
| .padding(padding) | |
| .padding(16.dp) | |
| ) { | |
| Text(text = "Buckaroo Time", style = MaterialTheme.typography.headlineMedium) | |
| Spacer(modifier = Modifier.height(16.dp)) | |
| Text(text = solarTimeState.value, style = MaterialTheme.typography.bodyLarge) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| private fun requestPermission(onResult: (Boolean) -> Unit) { | |
| val launcher = registerForActivityResult( | |
| ActivityResultContracts.RequestPermission() | |
| ) { granted -> onResult(granted) } | |
| when { | |
| ActivityCompat.checkSelfPermission( | |
| this, | |
| Manifest.permission.ACCESS_FINE_LOCATION | |
| ) == PackageManager.PERMISSION_GRANTED -> onResult(true) | |
| else -> launcher.launch(Manifest.permission.ACCESS_FINE_LOCATION) | |
| } | |
| } | |
| private fun calculateSolarTime(longitude: Double): String { | |
| val nowUtc = ZonedDateTime.now(ZoneOffset.UTC) | |
| val eotMinutes = equationOfTime(nowUtc.dayOfYear) | |
| val timeCorrectionMinutes = (4.0 * longitude) + eotMinutes | |
| val utcMinutesPastMidnight = nowUtc.hour * 60 + nowUtc.minute + nowUtc.second / 60.0 | |
| var latMinutesPastMidnight = utcMinutesPastMidnight + timeCorrectionMinutes | |
| latMinutesPastMidnight = (latMinutesPastMidnight % 1440 + 1440) % 1440 | |
| val latHours = (latMinutesPastMidnight / 60).toInt() | |
| val latMinutes = (latMinutesPastMidnight % 60).toInt() | |
| val latSeconds = ((latMinutesPastMidnight * 60) % 60).toInt() | |
| return String.format(Locale.US, "%02d:%02d:%02d", latHours, latMinutes, latSeconds) | |
| } | |
| private fun equationOfTime(dayOfYear: Int): Double { | |
| val b = 2 * Math.PI * (dayOfYear - 81) / 365 | |
| return 9.87 * sin(2 * b) - 7.53 * cos(b) - 1.5 * sin(b) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment