From 113103dedd2ea01eb861e9ed3daeb15851226163 Mon Sep 17 00:00:00 2001 From: SlaydeSequeira Date: Tue, 1 Oct 2024 16:10:37 +0530 Subject: [PATCH 1/4] I have added a retry to keep checking if internet has come back as well as i removed observers ondestroy so even though the audio will play in the background no other part of the activity will me running thus reducing power consumption --- .../java/me/tangobee/weathernaut/MainActivity.kt | 14 +++++++++----- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt b/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt index 313dded..80090da 100644 --- a/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt +++ b/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt @@ -9,6 +9,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.cancel import me.tangobee.weathernaut.data.RetrofitHelper @@ -49,14 +50,16 @@ class MainActivity : AppCompatActivity() { } } - noInternetLiveData.observe(this) {noInternet -> - if(noInternet) { - Toast.makeText(this, getString(R.string.no_internet), Toast.LENGTH_LONG).show() - Thread.sleep(1000) - exitProcess(0) + noInternetLiveData.observe(this) { noInternet -> + if (noInternet) { + Snackbar.make(binding.root, getString(R.string.no_internet), Snackbar.LENGTH_INDEFINITE) + .setAction(getString(R.string.retry)) { + fetchData() // Retry fetching data + }.show() } } + val settingsModel = SharedPreferencesHelper(this).getSettings() if(settingsModel?.isMusicOn != false) { val startMusicIntent = Intent(this, WeatherMusicService::class.java) @@ -112,6 +115,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/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 From f58c1d24c08642c12c007427cb4d35da68ab1159 Mon Sep 17 00:00:00 2001 From: SlaydeSequeira Date: Tue, 1 Oct 2024 16:12:07 +0530 Subject: [PATCH 2/4] I have added a retry to keep checking if internet has come back as well as i removed observers ondestroy so even though the audio will play in the background no other part of the activity will me running thus reducing power consumption --- app/src/main/java/me/tangobee/weathernaut/MainActivity.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt b/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt index 80090da..328429d 100644 --- a/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt +++ b/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt @@ -2,7 +2,6 @@ package me.tangobee.weathernaut import android.content.Intent import android.os.Bundle -import android.util.Log import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen From 801a7811d1b294427480ceb0ce809781f2e31ad1 Mon Sep 17 00:00:00 2001 From: SlaydeSequeira Date: Thu, 3 Oct 2024 10:11:26 +0530 Subject: [PATCH 3/4] I have added a retry to keep checking if internet has come back as well as i removed observers ondestroy so even though the audio will play in the background no other part of the activity will me running thus reducing power consumption along with that i added a notification for user to stop the music from the notification bar itself. i also addeda few toast messages to tell the user what the issue is. --- app/src/main/AndroidManifest.xml | 1 + .../me/tangobee/weathernaut/MainActivity.kt | 47 ++++++++++--- .../services/WeatherMusicService.kt | 69 ++++++++++++------- .../main/res/drawable/baseline_stop_24.xml | 5 ++ 4 files changed, 85 insertions(+), 37 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_stop_24.xml 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,10 +54,11 @@ 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) } } @@ -58,9 +72,13 @@ class MainActivity : AppCompatActivity() { } } + // 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) } @@ -71,6 +89,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) @@ -78,8 +103,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) @@ -87,12 +112,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) } } @@ -105,7 +130,7 @@ class MainActivity : AppCompatActivity() { val weatherHelper = WeatherHelper(currentSettings, weatherData) val newWeatherData = weatherHelper.convertWeatherData() - if(newWeatherData != weatherData) { + if (newWeatherData != weatherData) { settingsUpdated = true weatherViewModel.updateWeatherData(newWeatherData) } @@ -117,4 +142,4 @@ class MainActivity : AppCompatActivity() { 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 @@ + + + + + From 93e7c9e24b195bd708d1ce519321f8390cc3c1fb Mon Sep 17 00:00:00 2001 From: SlaydeSequeira Date: Thu, 3 Oct 2024 21:19:22 +0530 Subject: [PATCH 4/4] removed snackbar when no internet --- .../main/java/me/tangobee/weathernaut/MainActivity.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt b/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt index a9f35e2..efb29ee 100644 --- a/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt +++ b/app/src/main/java/me/tangobee/weathernaut/MainActivity.kt @@ -63,14 +63,7 @@ class MainActivity : AppCompatActivity() { } } - noInternetLiveData.observe(this) { noInternet -> - if (noInternet) { - Snackbar.make(binding.root, getString(R.string.no_internet), Snackbar.LENGTH_INDEFINITE) - .setAction(getString(R.string.retry)) { - fetchData() // Retry fetching data - }.show() - } - } + // Request notification permission for Android 13+ (API 33+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {