292 lines
9.8 KiB
Kotlin
292 lines
9.8 KiB
Kotlin
package com.module.breeze
|
|
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.os.Build
|
|
import android.os.Bundle
|
|
import androidx.activity.ComponentActivity
|
|
import androidx.activity.compose.setContent
|
|
import androidx.activity.enableEdgeToEdge
|
|
import androidx.annotation.RequiresApi
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.size
|
|
import androidx.compose.foundation.shape.CircleShape
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.outlined.AcUnit
|
|
import androidx.compose.material.icons.outlined.Air
|
|
import androidx.compose.material.icons.outlined.ArrowDownward
|
|
import androidx.compose.material.icons.outlined.ArrowUpward
|
|
import androidx.compose.material.icons.outlined.Map
|
|
import androidx.compose.material.icons.outlined.Settings
|
|
import androidx.compose.material.icons.outlined.WaterDrop
|
|
import androidx.compose.material3.Button
|
|
import androidx.compose.material3.ButtonDefaults
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.material3.Scaffold
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import androidx.compose.runtime.remember
|
|
import androidx.compose.runtime.setValue
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.draw.alpha
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.graphics.vector.ImageVector
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.tooling.preview.Preview
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.unit.sp
|
|
import com.module.breeze.ui.theme.BreezeTheme
|
|
import kotlinx.coroutines.runBlocking
|
|
import java.text.DecimalFormat
|
|
import java.time.LocalDate
|
|
import kotlin.math.roundToInt
|
|
import android.Manifest
|
|
import android.content.pm.PackageManager
|
|
import android.location.Location
|
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.material3.Button
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.runtime.DisposableEffect
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import androidx.compose.runtime.remember
|
|
import androidx.compose.runtime.setValue
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.core.content.ContextCompat
|
|
|
|
|
|
val fontSizeCurrentTemp = 48.sp
|
|
val fontSizeUpper = 16.sp
|
|
val fontSizeTitle = 22.sp
|
|
val breezeFontWeight = FontWeight.Bold
|
|
val iconStyle = Icons.Outlined;
|
|
val textColor = Color(0, 0, 0)
|
|
val numberFormat = DecimalFormat("00")
|
|
val apiKey = "8a6090c4308455152cd8c677b802883b"
|
|
|
|
class MainActivity : ComponentActivity() {
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
enableEdgeToEdge()
|
|
setContent {
|
|
BreezeTheme {
|
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
|
WeatherInfo(
|
|
modifier = Modifier.padding(innerPadding)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
fun openSettings(ctx: Context) {
|
|
var intent = Intent(ctx, SettingsActivity::class.java)
|
|
ctx.startActivity(intent)
|
|
}
|
|
}
|
|
}
|
|
|
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
fun getTodaysLocation(ctx: Context): String {
|
|
var dayOfTheWeek = LocalDate.now().dayOfWeek.value - 1
|
|
var plz = "Loading ..."
|
|
runBlocking {
|
|
plz = getLocations(ctx)[dayOfTheWeek]
|
|
}
|
|
return plz
|
|
}
|
|
|
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
fun fetchWeather(ctx: Context): ForecastSummary {
|
|
var todayPlz = getTodaysLocation(ctx)
|
|
|
|
var data: ForecastSummary
|
|
runBlocking {
|
|
val forecastResponse = RetrofitClient.instance.getWeatherForecast("${todayPlz},ch", apiKey)
|
|
val todaysForecast = getTodaysForecast(forecastResponse)
|
|
|
|
data = todaysForecast
|
|
println("Today's Forecast for ${todayPlz}:")
|
|
println("Min Temperature: ${todaysForecast.minTemp}°C")
|
|
println("Max Temperature: ${todaysForecast.maxTemp}°C")
|
|
println("Rain: ${if (todaysForecast.hasRain) "Yes" else "No"}")
|
|
println("Snow: ${if (todaysForecast.hasSnow) "Yes" else "No"}")
|
|
println("Strong Winds: ${if (todaysForecast.hasStrongWinds) "Yes" else "No"}")
|
|
}
|
|
return data
|
|
}
|
|
|
|
fun fetchCurrentTemp(ctx: Context, lat: Double, lon: Double): WeatherResponse {
|
|
var data: WeatherResponse
|
|
runBlocking {
|
|
data = RetrofitClient.instance.getCurrentWeather(lat, lon, apiKey)
|
|
}
|
|
return data
|
|
}
|
|
|
|
|
|
@Composable
|
|
fun WeatherInfo(modifier: Modifier = Modifier) {
|
|
val ctx = LocalContext.current
|
|
var forecast by remember { mutableStateOf(ForecastSummary(0.0, 0.0, false, false, false)) }
|
|
var currentTemp by remember {
|
|
mutableStateOf(
|
|
WeatherResponse(Main(0.0, 0.0, 0.0, 0.0, 0, 0), emptyList(), 0L)
|
|
)
|
|
}
|
|
val locationHelper = remember { GpsRepository(ctx) }
|
|
|
|
var location by remember { mutableStateOf<Location?>(null) }
|
|
|
|
val locationPermission = ContextCompat.checkSelfPermission(
|
|
ctx, Manifest.permission.ACCESS_FINE_LOCATION
|
|
)
|
|
var hasLocationPermission by remember {
|
|
mutableStateOf(locationPermission == PackageManager.PERMISSION_GRANTED)
|
|
}
|
|
|
|
val permissionLauncher = rememberLauncherForActivityResult(
|
|
ActivityResultContracts.RequestPermission()
|
|
) { isGranted -> hasLocationPermission = isGranted }
|
|
|
|
|
|
DisposableEffect(key1 = locationHelper, key2 = hasLocationPermission) {
|
|
if (hasLocationPermission) {
|
|
locationHelper.startListening { newLocation ->
|
|
location = newLocation
|
|
}
|
|
}
|
|
|
|
onDispose {
|
|
locationHelper.stopListening()
|
|
}
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
forecast = fetchWeather(ctx)
|
|
}
|
|
|
|
|
|
Navigation(
|
|
iconStyle.Settings, "Settings Icon", onClick = { MainActivity.openSettings(ctx) })
|
|
|
|
Column(
|
|
modifier = Modifier.fillMaxSize(),
|
|
verticalArrangement = Arrangement.Center,
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
) {
|
|
Row(verticalAlignment = Alignment.Bottom) {
|
|
Icon(
|
|
imageVector = iconStyle.Map,
|
|
contentDescription = "Location Icon",
|
|
modifier = Modifier
|
|
.padding(end = 16.dp)
|
|
.size(32.dp)
|
|
)
|
|
Icon(
|
|
imageVector = iconStyle.WaterDrop,
|
|
contentDescription = "Rain Icon",
|
|
modifier = Modifier.alpha(if (forecast.hasRain) 1F else 0.2F)
|
|
)
|
|
Icon(
|
|
imageVector = iconStyle.AcUnit,
|
|
contentDescription = "Snow Icon",
|
|
modifier = Modifier.alpha(if (forecast.hasSnow) 1F else 0.2F)
|
|
)
|
|
|
|
Icon(
|
|
imageVector = iconStyle.Air,
|
|
contentDescription = "Wind Icon",
|
|
modifier = Modifier.alpha(if (forecast.hasStrongWinds) 1F else 0.2F)
|
|
)
|
|
}
|
|
Row {
|
|
Icon(
|
|
imageVector = iconStyle.ArrowDownward, contentDescription = "Arrow down Icon"
|
|
)
|
|
Text(
|
|
text = numberFormat.format(forecast.minTemp.roundToInt()) + "°",
|
|
fontSize = fontSizeUpper,
|
|
fontWeight = breezeFontWeight
|
|
)
|
|
Icon(
|
|
imageVector = iconStyle.ArrowUpward, contentDescription = "Arrow up Icon"
|
|
)
|
|
Text(
|
|
text = numberFormat.format(forecast.maxTemp.roundToInt()) + "°",
|
|
fontSize = fontSizeUpper,
|
|
fontWeight = breezeFontWeight
|
|
)
|
|
}
|
|
if (hasLocationPermission) {
|
|
location?.let {
|
|
currentTemp = fetchCurrentTemp(ctx, it.latitude, it.latitude)
|
|
}
|
|
Text(
|
|
text = numberFormat.format(currentTemp.main.temp.roundToInt()) + "°",
|
|
fontSize = fontSizeCurrentTemp,
|
|
fontWeight = breezeFontWeight,
|
|
)
|
|
} else {
|
|
Button(
|
|
onClick = {
|
|
permissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
|
},
|
|
colors = ButtonDefaults.buttonColors(
|
|
containerColor = Color(0, 0, 0, 20), contentColor = textColor
|
|
),
|
|
) {
|
|
Text(
|
|
text = "Grant Permission",
|
|
fontWeight = breezeFontWeight,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Preview(showBackground = true)
|
|
@Composable
|
|
fun GreetingPreview() {
|
|
BreezeTheme {
|
|
WeatherInfo()
|
|
}
|
|
}
|
|
|
|
|
|
@Composable
|
|
fun Navigation(icon: ImageVector, iconDescription: String, onClick: () -> Unit = {}) {
|
|
Row(
|
|
horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()
|
|
) {
|
|
Button(
|
|
onClick = onClick,
|
|
colors = ButtonDefaults.buttonColors(
|
|
containerColor = Color(255, 0, 0, 0), contentColor = textColor
|
|
),
|
|
shape = CircleShape,
|
|
contentPadding = PaddingValues(0.dp),
|
|
modifier = Modifier.padding(0.dp, 40.dp, 28.dp, 0.dp)
|
|
) {
|
|
Icon(
|
|
imageVector = icon,
|
|
contentDescription = iconDescription,
|
|
modifier = Modifier.size(40.dp)
|
|
)
|
|
}
|
|
}
|
|
}
|