notification on lower level android working

This commit is contained in:
2024-02-18 12:38:38 +00:00
parent 75c7fa364e
commit ba630d1d2c
14 changed files with 193 additions and 32 deletions

View File

@@ -30,7 +30,7 @@
<receiver
android:name=".service.notification.NotificationReceiver"
android:exported="true"/>
android:exported="false"/>
</application>
</manifest>

View File

@@ -0,0 +1,46 @@
package com.appttude.h_mal.atlas_weather.application
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.appttude.h_mal.atlas_weather.data.WeatherSource
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
import com.appttude.h_mal.atlas_weather.service.notification.NotificationService
import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
import com.appttude.h_mal.atlas_weather.viewmodel.SettingsViewModel
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
class ApplicationViewModelFactory(
private val application: Application,
private val locationProvider: LocationProvider,
private val source: WeatherSource,
private val settingsRepository: SettingsRepository,
private val notificationService: NotificationService
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
with(modelClass) {
return when {
isAssignableFrom(WorldViewModel::class.java) -> WorldViewModel(
locationProvider,
source
)
isAssignableFrom(MainViewModel::class.java) -> MainViewModel(
locationProvider,
source
)
isAssignableFrom(SettingsViewModel::class.java) -> SettingsViewModel(
application, locationProvider, source, settingsRepository, notificationService
)
else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T
}
}
}

View File

@@ -4,6 +4,7 @@ import com.appttude.h_mal.atlas_weather.service.notification.NotificationHelper
import com.appttude.h_mal.atlas_weather.service.notification.NotificationService
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.provider
import org.kodein.di.generic.singleton
@@ -20,12 +21,26 @@ open class AtlasApp : AppClass() {
}
bind() from singleton {
NotificationService(this@AtlasApp).let { notificationService = it }
NotificationService(this@AtlasApp).apply { notificationService = this }
}
bind() from provider {
ApplicationViewModelFactory(
this@AtlasApp,
instance(),
instance(),
instance(),
instance()
)
}
}
override fun onCreate() {
super.onCreate()
notificationService.schedulePushNotifications()
}
// override fun onCreate() {
// super.onCreate()
// notificationService.schedulePushNotifications()
// }
fun scheduleNotifications() = notificationService.schedulePushNotifications()
fun unscheduleNotifications() = notificationService.unschedulePushNotifications()
}

View File

@@ -2,6 +2,7 @@ package com.appttude.h_mal.atlas_weather.service.notification
import android.Manifest
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.TaskStackBuilder
@@ -10,8 +11,11 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Build
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.MainActivity
import com.appttude.h_mal.atlas_weather.utils.displayToast
@@ -30,9 +34,10 @@ import org.kodein.di.generic.instance
* Updated by h_mal on 27/11/2020
*/
const val NOTIFICATION_CHANNEL_ID = "my_notification_channel_1"
const val NOTIFICATION_ID = 505
class NotificationReceiver : BroadcastReceiver() {
private val kodein = LateInitKodein()
private val helper: NotificationHelper by kodein.instance()
@@ -64,22 +69,51 @@ class NotificationReceiver : BroadcastReceiver() {
addNextIntent(notificationIntent)
}
val pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
val bmp: Bitmap = runBlocking { Picasso.get().load(weather.current?.icon).get() }
val notification = builder.setContentTitle("Weather App")
.setContentText(weather.current?.main + "°C")
.setSmallIcon(R.mipmap.ic_notif) //change icon
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_notif)
.setLargeIcon(bmp)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.build()
builder.setChannelId(NOTIFICATION_CHANNEL_ID)
.setAutoCancel(true)
.setContentTitle("My notification")
.setContentText("Much longer text that cannot fit one line...")
.setStyle(NotificationCompat.BigTextStyle()
.bigText("Much longer text that cannot fit one line..."))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
// Deliver notification
val notificationManager =
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is not in the Support Library.
val name = context.getString(R.string.channel_name)
val descriptionText = context.getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system.
val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, notification)
notificationManager.createNotificationChannel(channel)
with(NotificationManagerCompat.from(context)) {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>,
// grantResults: IntArray)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return@with
}
// notificationId is a unique int for each notification that you must define.
notify(NOTIFICATION_ID, builder.build())
}
}
}
}

View File

@@ -32,6 +32,8 @@ class NotificationService(context: Context) {
AlarmManager.INTERVAL_DAY,
alarmPendingIntent
)
// alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(calendar.timeInMillis, alarmPendingIntent), alarmPendingIntent)
}
fun unschedulePushNotifications() {

View File

@@ -1,7 +1,9 @@
package com.appttude.h_mal.atlas_weather.ui.home
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.POST_NOTIFICATIONS
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
@@ -10,9 +12,8 @@ import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation.findNavController
import androidx.navigation.ui.onNavDestinationSelected
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
import com.appttude.h_mal.atlas_weather.application.AtlasApp
import com.appttude.h_mal.atlas_weather.base.BaseFragment
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
@@ -56,6 +57,8 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
})
forecast_listview.adapter = recyclerAdapter
scheduleNotification()
}
@SuppressLint("MissingPermission")
@@ -99,6 +102,14 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
onRequestPermissionsResult(requestCode, grantResults)
}
fun scheduleNotification() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
sendNotification()
} else {
(requireActivity().application as AtlasApp).scheduleNotifications()
}
}
@SuppressLint("MissingPermission")
@NeedsPermission(ACCESS_COARSE_LOCATION)
fun showLocation() {
@@ -123,4 +134,29 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
fun onLocationNeverAskAgain() {
displayToast("Location permissions have been to never ask again")
}
@SuppressLint("MissingPermission")
@NeedsPermission(POST_NOTIFICATIONS)
fun sendNotification() {
(requireActivity().application as AtlasApp).scheduleNotifications()
}
@OnShowRationale(POST_NOTIFICATIONS)
fun showRationaleForNotification(request: PermissionRequest) {
// PermissionsDeclarationDialog(requireContext()).showDialog({
// request.proceed()
// }, {
// request.cancel()
// })
}
@OnPermissionDenied(POST_NOTIFICATIONS)
fun onNotificationDenied() {
displayToast("Notification permissions have been denied")
}
@OnNeverAskAgain(POST_NOTIFICATIONS)
fun onNotificationNeverAskAgain() {
displayToast("Notification permissions have been to never ask again")
}
}

View File

@@ -9,14 +9,12 @@ import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.google.gson.Gson
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.androidXModule
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.provider
import org.kodein.di.generic.singleton
abstract class BaseAppClass : Application(), KodeinAware {
@@ -40,7 +38,6 @@ abstract class BaseAppClass : Application(), KodeinAware {
bind() from singleton { SettingsRepositoryImpl(instance()) }
bind() from singleton { ServicesHelper(instance(), instance(), instance()) }
bind() from singleton { WeatherSource(instance(), instance()) }
bind() from provider { ApplicationViewModelFactory(this@BaseAppClass, instance(), instance(),instance()) }
}
open val flavourModule = Kodein.Module("Flavour") {

View File

@@ -8,8 +8,6 @@ import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInt
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
const val LOCATION_PERMISSION_REQUEST = 505
open class AppClass : BaseAppClass() {
override fun createNetworkModule(): WeatherApi {

View File

@@ -8,7 +8,7 @@ import androidx.fragment.app.createViewModelLazy
import com.appttude.h_mal.atlas_weather.base.baseViewModels.BaseViewModel
import com.appttude.h_mal.atlas_weather.helper.GenericsHelper.getGenericClassAt
import com.appttude.h_mal.atlas_weather.model.ViewState
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.application.ApplicationViewModelFactory
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance

View File

@@ -6,11 +6,10 @@ import androidx.annotation.XmlRes
import androidx.fragment.app.createViewModelLazy
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.base.baseViewModels.BaseAndroidViewModel
import com.appttude.h_mal.atlas_weather.helper.GenericsHelper.getGenericClassAt
import com.appttude.h_mal.atlas_weather.model.ViewState
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.application.ApplicationViewModelFactory
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance

View File

@@ -34,6 +34,8 @@
<string name="unit_key">Units</string>
<string name="widget_black_background">widget_black_background</string>
<string name="weather_units">Weather units</string>
<string name="channel_name">channel name</string>
<string name="channel_description">channel description</string>
<string-array name="units">
<item>Metric</item>

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.viewmodel
package com.appttude.h_mal.atlas_weather.application
import android.app.Application
import androidx.lifecycle.ViewModel
@@ -6,6 +6,9 @@ import androidx.lifecycle.ViewModelProvider
import com.appttude.h_mal.atlas_weather.data.WeatherSource
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
import com.appttude.h_mal.atlas_weather.viewmodel.SettingsViewModel
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
class ApplicationViewModelFactory(

View File

@@ -1,6 +1,20 @@
import com.appttude.h_mal.atlas_weather.application.AppClass
import com.appttude.h_mal.atlas_weather.application.ApplicationViewModelFactory
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.provider
import org.kodein.di.generic.singleton
open class MonoApp : AppClass() {
override val flavourModule = super.flavourModule.copy {
bind() from provider {
ApplicationViewModelFactory(
this@MonoApp,
instance(),
instance(),
instance(),
)
}
}
}

View File

@@ -13,7 +13,6 @@ import com.appttude.h_mal.atlas_weather.base.baseViewModels.BaseAndroidViewModel
import com.appttude.h_mal.atlas_weather.data.WeatherSource
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
import com.appttude.h_mal.atlas_weather.service.notification.NotificationHelper
import com.appttude.h_mal.atlas_weather.widget.NewAppWidget
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -24,12 +23,28 @@ class SettingsViewModel(
application: Application,
private val locationProvider: LocationProvider,
private val weatherSource: WeatherSource,
private val settingsRepository: SettingsRepository,
private val notificationHelper: NotificationHelper
private val settingsRepository: SettingsRepository
) : BaseAndroidViewModel(application) {
private fun getContext() = getApplication<BaseAppClass>().applicationContext
fun updateWidget() {
val context = getContext()
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
val widgetManager = AppWidgetManager.getInstance(context)
val ids =
widgetManager.getAppWidgetIds(
ComponentName(
context,
NewAppWidget::class.java
)
)
AppWidgetManager.getInstance(context)
.notifyAppWidgetViewDataChanged(ids, R.id.whole_widget_view)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
context.sendBroadcast(intent)
}
fun refreshWeatherData() {
onStart()
job = CoroutineScope(Dispatchers.IO).launch {