diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9c577a1..e761afd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
+ if (isGranted) {
+ Toast.makeText(this, "Notification permission granted", Toast.LENGTH_SHORT).show()
+ } else {
+ Toast.makeText(this, "Notification permission denied", Toast.LENGTH_SHORT).show()
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val splashScreen = installSplashScreen()
@@ -41,24 +54,24 @@ class MainActivity : AppCompatActivity() {
sharedPreferencesHelper = SharedPreferencesHelper(this)
- val noInternetLiveData : MutableLiveData = MutableLiveData(false)
+ val noInternetLiveData : MutableLiveData = MutableLiveData(false)
- coroutineExceptionHandler = CoroutineExceptionHandler{_, throwable ->
- if(throwable is UnknownHostException) {
+ // Handle internet-related errors
+ coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
+ if (throwable is UnknownHostException) {
noInternetLiveData.postValue(true)
}
}
- noInternetLiveData.observe(this) {noInternet ->
- if(noInternet) {
- Toast.makeText(this, getString(R.string.no_internet), Toast.LENGTH_LONG).show()
- Thread.sleep(1000)
- exitProcess(0)
- }
+
+
+ // Request notification permission for Android 13+ (API 33+)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requestNotificationPermission()
}
val settingsModel = SharedPreferencesHelper(this).getSettings()
- if(settingsModel?.isMusicOn != false) {
+ if (settingsModel?.isMusicOn != false) {
val startMusicIntent = Intent(this, WeatherMusicService::class.java)
startService(startMusicIntent)
}
@@ -69,6 +82,13 @@ class MainActivity : AppCompatActivity() {
setContentView(binding.root)
}
+ private fun requestNotificationPermission() {
+ if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
+ // Request notification permission
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }
+
private fun fetchData() {
val weatherService = RetrofitHelper.getInstance().create(WeatherService::class.java)
val weatherRepository = WeatherRepository(weatherService)
@@ -76,8 +96,8 @@ class MainActivity : AppCompatActivity() {
val geocodingData = sharedPreferencesHelper.getGeocodingData()
- if(weatherViewModel.weatherLiveData.value == null) {
- if(geocodingData == null) {
+ if (weatherViewModel.weatherLiveData.value == null) {
+ if (geocodingData == null) {
weatherViewModel.getWeather(coroutineExceptionHandler)
} else {
weatherViewModel.getGeoWeather(coroutineExceptionHandler, geocodingData)
@@ -85,12 +105,12 @@ class MainActivity : AppCompatActivity() {
}
weatherViewModel.weatherLiveData.observe(this) { weatherData ->
- if(weatherData == null) {
+ if (weatherData == null) {
Toast.makeText(this, getString(R.string.api_fetching_error), Toast.LENGTH_SHORT).show()
Thread.sleep(1000)
exitProcess(0)
} else {
- if(!settingsUpdated) {
+ if (!settingsUpdated) {
createLocalDB(weatherData)
}
}
@@ -103,7 +123,7 @@ class MainActivity : AppCompatActivity() {
val weatherHelper = WeatherHelper(currentSettings, weatherData)
val newWeatherData = weatherHelper.convertWeatherData()
- if(newWeatherData != weatherData) {
+ if (newWeatherData != weatherData) {
settingsUpdated = true
weatherViewModel.updateWeatherData(newWeatherData)
}
@@ -112,6 +132,7 @@ class MainActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
+ weatherViewModel.weatherLiveData.removeObservers(this)
weatherViewModel.viewModelScope.cancel("ActivityDestroying")
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/me/tangobee/weathernaut/services/WeatherMusicService.kt b/app/src/main/java/me/tangobee/weathernaut/services/WeatherMusicService.kt
index 2f4a21a..1b70516 100644
--- a/app/src/main/java/me/tangobee/weathernaut/services/WeatherMusicService.kt
+++ b/app/src/main/java/me/tangobee/weathernaut/services/WeatherMusicService.kt
@@ -3,12 +3,13 @@ package me.tangobee.weathernaut.services
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
+import android.app.PendingIntent
import android.app.Service
import android.content.Intent
-import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
+import android.content.pm.ServiceInfo
import android.media.MediaPlayer
-import android.os.IBinder
import android.os.Build
+import android.os.IBinder
import androidx.core.app.NotificationCompat
import me.tangobee.weathernaut.R
@@ -20,63 +21,68 @@ class WeatherMusicService : Service() {
private val musicUrl = "https://weathernaut-backend.onrender.com/api/music"
private val SERVICE_ID = 1
+ private val CHANNEL_ID = "MUSIC_SERVICE_CHANNEL"
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
createNotificationChannel()
- // Foreground notification
- val notification: Notification = NotificationCompat.Builder(this, "MUSIC_SERVICE_CHANNEL")
+ // Check if the service was called with the "STOP_MUSIC" action to stop the music
+ if (intent?.action == "STOP_MUSIC") {
+ stopSelf() // Stop the service and the music
+ return START_NOT_STICKY
+ }
+
+ // Intent for stopping the music through notification action
+ val stopMusicIntent = Intent(this, WeatherMusicService::class.java).apply {
+ action = "STOP_MUSIC"
+ }
+ val stopMusicPendingIntent = PendingIntent.getService(
+ this, 0, stopMusicIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+
+ // Foreground notification with action to stop music
+ val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Playing Weather Music")
.setContentText("Enjoy the music and your day's weather!")
- .setSmallIcon(R.drawable.icon_music)
+ .setSmallIcon(R.drawable.icon_music) // Replace with your own icon
+ .addAction(R.drawable.baseline_stop_24, "Stop Music", stopMusicPendingIntent) // Action to stop music
.build()
+ // Start the service in the foreground with the notification
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
startForeground(SERVICE_ID, notification)
} else {
- startForeground(SERVICE_ID, notification,
- FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
+ startForeground(SERVICE_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
}
+ // Initialize or restart the media player
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer().apply {
setDataSource(musicUrl)
- prepareAsync()
+ prepareAsync() // Async preparation for streaming
setOnPreparedListener {
- it.start()
+ it.start() // Start playing music when ready
}
setOnCompletionListener {
- start()
+ start() // Loop the music when it ends
}
setOnErrorListener { _, _, _ ->
- stopSelf()
+ stopSelf() // Stop service on error
false
}
}
} else if (!mediaPlayer!!.isPlaying) {
- mediaPlayer!!.start()
+ mediaPlayer!!.start() // Resume music if it's paused
}
return START_STICKY
}
- override fun onDestroy() {
- super.onDestroy()
- if (mediaPlayer != null) {
- mediaPlayer?.stop()
- mediaPlayer?.release()
- mediaPlayer = null
- }
- }
-
- override fun onBind(intent: Intent?): IBinder? {
- return null // We don't bind this service to an activity
- }
-
+ // Create notification channel for Android 8.0 and above
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
- "MUSIC_SERVICE_CHANNEL",
+ CHANNEL_ID,
"Music Playback Channel",
NotificationManager.IMPORTANCE_LOW
)
@@ -84,4 +90,15 @@ class WeatherMusicService : Service() {
manager?.createNotificationChannel(serviceChannel)
}
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+ mediaPlayer?.stop()
+ mediaPlayer?.release()
+ mediaPlayer = null
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null // We don't bind this service to an activity
+ }
}
diff --git a/app/src/main/res/drawable/baseline_stop_24.xml b/app/src/main/res/drawable/baseline_stop_24.xml
new file mode 100644
index 0000000..817d57b
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_stop_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8c45fef..0c20ec7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
Weathernaut
-
+ Retry
Something went wrong!\nDon\'t worry its not you, its us. Try later.
No Internet Connection
Search Cities Button