- mid commit

This commit is contained in:
2024-02-06 16:42:03 +00:00
parent 9df83eb083
commit af72205f86
21 changed files with 255 additions and 230 deletions

View File

@@ -2,8 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"
android:name="com.appttude.h_mal.atlas_weather.application.AtlasApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@@ -27,23 +29,8 @@
</activity>
<receiver
android:name=".notification.NotificationReceiver"
android:exported="true"
android:parentActivityName=".MainActivity" />
<receiver
android:name=".widget.NewAppWidget"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="com.example.h_mal.weather_app.app.ACTION_DATA_UPDATED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/new_app_widget_info" />
</receiver>
android:name=".service.notification.NotificationReceiver"
android:exported="true"/>
</application>
</manifest>

View File

@@ -1,8 +0,0 @@
package com.appttude.h_mal.atlas_weather.notification
import android.graphics.Bitmap
data class NotificationData(
val temp: String,
val icon: Bitmap
)

View File

@@ -1,72 +0,0 @@
package com.appttude.h_mal.atlas_weather.notification
import android.Manifest
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.MainActivity
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
import com.appttude.h_mal.atlas_weather.utils.displayToast
import org.kodein.di.KodeinAware
import org.kodein.di.LateInitKodein
import org.kodein.di.generic.instance
/**
* Created by h_mal on 29/04/2018.
* Updated by h_mal on 27/11/2020
*/
const val NOTIFICATION_CHANNEL_ID = "my_notification_channel_1"
class NotificationReceiver : BroadcastReceiver() {
private val kodein = LateInitKodein()
private val helper: ServicesHelper by kodein.instance()
override fun onReceive(context: Context, intent: Intent) {
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
context.displayToast("Please enable location permissions")
return
}
// notification validation
}
private fun pushNotif(context: Context?, weather: FullWeather) {
val notificationIntent = Intent(context, MainActivity::class.java)
val stackBuilder = TaskStackBuilder.create(context).apply {
addParentStack(MainActivity::class.java)
addNextIntent(notificationIntent)
}
val pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
val notification = builder.setContentTitle("Weather App")
.setContentText(weather.current?.main + "°C")
.setSmallIcon(R.mipmap.ic_notif) //change icon
// .setLargeIcon(Icon.createWithResource(context, getImageResource(forecastItem.getCurrentForecast().getIconURL(), context)))
.setAutoCancel(true)
.setContentIntent(pendingIntent).build()
builder.setChannelId(NOTIFICATION_CHANNEL_ID)
val notificationManager =
context!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, notification)
}
}

View File

@@ -0,0 +1,26 @@
package com.appttude.h_mal.atlas_weather.service.notification
import android.Manifest
import androidx.annotation.RequiresPermission
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.model.weather.FullWeather
class NotificationHelper(
private val weatherSource: WeatherSource,
private val locationProvider: LocationProvider
) {
@RequiresPermission(value = Manifest.permission.ACCESS_COARSE_LOCATION)
suspend fun fetchData(): FullWeather? {
return try {
// Get location
val latLong = locationProvider.getCurrentLatLong()
weatherSource.getWeather(latLon = latLong)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}

View File

@@ -0,0 +1,85 @@
package com.appttude.h_mal.atlas_weather.service.notification
import android.Manifest
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
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
import com.squareup.picasso.Picasso
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.kodein.di.KodeinAware
import org.kodein.di.LateInitKodein
import org.kodein.di.generic.instance
/**
* Created by h_mal on 29/04/2018.
* Updated by h_mal on 27/11/2020
*/
const val NOTIFICATION_CHANNEL_ID = "my_notification_channel_1"
class NotificationReceiver : BroadcastReceiver() {
private val kodein = LateInitKodein()
private val helper: NotificationHelper by kodein.instance()
override fun onReceive(context: Context, intent: Intent) {
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
pushNotification(context)
} else {
context.displayToast("Please enable location permissions")
}
}
@RequiresPermission(value = Manifest.permission.ACCESS_COARSE_LOCATION)
private fun pushNotification(context: Context) {
CoroutineScope(Dispatchers.IO).launch {
// Retrieve weather data
val weather = runBlocking { helper.fetchData() } ?: return@launch
// Build notification
val notificationIntent = Intent(context, MainActivity::class.java)
val stackBuilder = TaskStackBuilder.create(context).apply {
addParentStack(MainActivity::class.java)
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
.setLargeIcon(bmp)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.build()
builder.setChannelId(NOTIFICATION_CHANNEL_ID)
// Deliver notification
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, notification)
}
}
}

View File

@@ -0,0 +1,66 @@
package com.appttude.h_mal.atlas_weather.service.notification
import android.app.AlarmManager
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Context.ALARM_SERVICE
import android.content.Intent
import android.icu.util.Calendar
import android.icu.util.GregorianCalendar
import androidx.core.app.NotificationManagerCompat
private const val HOUR_TO_SHOW_PUSH = 6
class NotificationService(context: Context) {
private val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager
private val alarmPendingIntent by lazy {
val intent = Intent(context, NotificationReceiver::class.java)
PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
}
private val notificationManager = NotificationManagerCompat.from(context)
fun schedulePushNotifications() {
val calendar = getCalendarForNotification()
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
AlarmManager.INTERVAL_DAY,
alarmPendingIntent
)
}
fun unschedulePushNotifications() {
alarmManager.cancel(alarmPendingIntent)
}
fun areNotificationsEnabled() = when {
notificationManager.areNotificationsEnabled().not() -> false
else -> {
notificationManager.notificationChannels.firstOrNull { channel ->
channel.importance == NotificationManager.IMPORTANCE_NONE
} == null
}
}
private fun getCalendarForNotification(): Calendar {
// return GregorianCalendar.getInstance().apply {
// if (get(Calendar.HOUR_OF_DAY) >= HOUR_TO_SHOW_PUSH) {
// add(Calendar.DAY_OF_MONTH, 1)
// }
//
// set(Calendar.HOUR_OF_DAY, HOUR_TO_SHOW_PUSH)
// set(Calendar.MINUTE, 0)
// set(Calendar.SECOND, 0)
// set(Calendar.MILLISECOND, 0)
// }
return GregorianCalendar.getInstance().apply {
add(Calendar.MINUTE, 1)
}
}
}

View File

@@ -1,73 +1,61 @@
package com.appttude.h_mal.atlas_weather.ui.settings
import android.app.AlarmManager
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.notification.NotificationReceiver
import com.appttude.h_mal.atlas_weather.widget.NewAppWidget
import java.util.Calendar
import com.appttude.h_mal.atlas_weather.base.BasePreferencesFragment
import com.appttude.h_mal.atlas_weather.utils.displayToast
import com.appttude.h_mal.atlas_weather.viewmodel.SettingsViewModel
class SettingsFragment : PreferenceFragmentCompat() {
class SettingsFragment : BasePreferencesFragment<SettingsViewModel>(R.xml.prefs) {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.prefs, rootKey)
override fun preferenceChanged(key: String) {
when (key) {
//listener on changed sort order preference:
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
prefs.registerOnSharedPreferenceChangeListener { _, key ->
if (key == "temp_units") {
val intent = Intent(requireContext(), NewAppWidget::class.java)
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
val ids = AppWidgetManager.getInstance(requireContext())
.getAppWidgetIds(ComponentName(requireContext(), NewAppWidget::class.java))
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
requireContext().sendBroadcast(intent)
}
if (key == "notif_boolean") {
setupNotificationBroadcaster(requireContext())
}
if (key == "widget_black_background") {
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
val widgetManager = AppWidgetManager.getInstance(requireContext())
val ids =
widgetManager.getAppWidgetIds(
ComponentName(
requireContext(),
NewAppWidget::class.java
)
)
AppWidgetManager.getInstance(requireContext())
.notifyAppWidgetViewDataChanged(ids, R.id.whole_widget_view)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
requireContext().sendBroadcast(intent)
"temp_units" -> viewModel.refreshWeatherData()
"notif_boolean" -> {
// TODO: update notification
// viewModel.updateWidget()
// displayToast("Widget background has been updates")
}
}
}
fun setupNotificationBroadcaster(context: Context) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val notificationIntent = Intent(context, NotificationReceiver::class.java)
val broadcast = PendingIntent.getBroadcast(
context, 100, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val cal: Calendar = Calendar.getInstance()
cal.set(Calendar.HOUR_OF_DAY, 6)
cal.set(Calendar.MINUTE, 8)
cal.set(Calendar.SECOND, 5)
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
cal.timeInMillis,
AlarmManager.INTERVAL_DAY,
broadcast
)
override fun onSuccess(data: Any?) {
super.onSuccess(data)
if (data is String) displayToast(data)
}
}
// override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
// setPreferencesFromResource(R.xml.prefs, rootKey)
//
// //listener on changed sort order preference:
// val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
// prefs.registerOnSharedPreferenceChangeListener { _, key ->
// if (key == "temp_units") {
//
// }
// if (key == "notif_boolean") {
// setupNotificationBroadcaster(requireContext())
// }
// }
// }
//
// fun setupNotificationBroadcaster(context: Context) {
// val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// val notificationIntent = Intent(context, NotificationReceiver::class.java)
// val broadcast = PendingIntent.getBroadcast(
// context, 100, notificationIntent,
// PendingIntent.FLAG_UPDATE_CURRENT
// )
// val cal: Calendar = Calendar.getInstance()
// cal.set(Calendar.HOUR_OF_DAY, 6)
// cal.set(Calendar.MINUTE, 8)
// cal.set(Calendar.SECOND, 5)
// alarmManager.setRepeating(
// AlarmManager.RTC_WAKEUP,
// cal.timeInMillis,
// AlarmManager.INTERVAL_DAY,
// broadcast
// )
// }
//}

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:configure="com.appttude.h_mal.atlas_weather.ui.widget.WidgetLocationPermissionActivity"
android:initialKeyguardLayout="@layout/weather_app_widget"
android:initialLayout="@layout/weather_app_widget"
android:minWidth="320.0dp"
android:minHeight="110.0dp"
android:minResizeWidth="320.0dp"
android:minResizeHeight="110.0dp"
android:previewImage="@drawable/widget_screenshot"
android:resizeMode="vertical"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen">
</appwidget-provider>

View File

@@ -1,42 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <PreferenceCategory android:title="Units">-->
<!-- <ListPreference-->
<!-- android:defaultValue="°C"-->
<!-- android:entries="@array/list_preference_temp"-->
<!-- android:entryValues="@array/list_preference_temp"-->
<!-- android:key="temp_units"-->
<!-- android:title="Temperature Units" />-->
<!-- <ListPreference-->
<!-- android:defaultValue="kph"-->
<!-- android:entries="@array/list_preference_wind"-->
<!-- android:entryValues="@array/list_preference_wind_values"-->
<!-- android:key="wind_units"-->
<!-- android:title="Wind Units" />-->
<!-- <ListPreference-->
<!-- android:defaultValue="mm"-->
<!-- android:entries="@array/list_preference_precip"-->
<!-- android:entryValues="@array/list_preference_precip_values"-->
<!-- android:key="precip_units"-->
<!-- android:title="Precipitation Units" />-->
<!-- <ListPreference-->
<!-- android:defaultValue="km"-->
<!-- android:entries="@array/list_preference_vis"-->
<!-- android:entryValues="@array/list_preference_vis_values"-->
<!-- android:key="vis_units"-->
<!-- android:title="Visibility Units" />-->
<!-- </PreferenceCategory>-->
<ListPreference
android:title="@string/weather_units"
android:entries="@array/units"
android:entryValues="@array/units"
android:defaultValue="Metric"
android:key="UnitType"
/>
<PreferenceCategory android:title="Notification Settings">
<SwitchPreference
android:defaultValue="true"
android:key="notif_boolean"
android:title="Notification" />
</PreferenceCategory>
<PreferenceCategory android:title="Widget Settings">
<SwitchPreference
android:defaultValue="false"
android:key="widget_black_background"
android:title="Set widget background black" />
android:title="Notifications enabled" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -21,8 +21,13 @@ import org.kodein.di.generic.singleton
abstract class BaseAppClass : Application(), KodeinAware {
// Kodein creation of modules to be retrieve within the app
// Kodein aware to initialise the classes used for DI
override val kodein = Kodein.lazy {
import(parentModule)
import(flavourModule)
}
val parentModule = Kodein.Module("Parent Module") {
import(androidXModule(this@BaseAppClass))
bind() from singleton { createNetworkModule() }
@@ -38,6 +43,10 @@ abstract class BaseAppClass : Application(), KodeinAware {
bind() from provider { ApplicationViewModelFactory(this@BaseAppClass, instance(), instance(),instance()) }
}
open val flavourModule = Kodein.Module("Flavour") {
import(parentModule)
}
abstract fun createNetworkModule(): WeatherApi
abstract fun createLocationModule(): LocationProvider
abstract fun createRoomDatabase(): AppDatabase

View File

@@ -10,7 +10,7 @@ import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
const val LOCATION_PERMISSION_REQUEST = 505
class AppClass : BaseAppClass() {
open class AppClass : BaseAppClass() {
override fun createNetworkModule(): WeatherApi {
return NetworkModule().invoke<WeatherApi>(

View File

@@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"
android:name="com.appttude.h_mal.atlas_weather.application.MonoApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"

View File

@@ -13,6 +13,7 @@ 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
@@ -23,28 +24,12 @@ class SettingsViewModel(
application: Application,
private val locationProvider: LocationProvider,
private val weatherSource: WeatherSource,
private val settingsRepository: SettingsRepository
private val settingsRepository: SettingsRepository,
private val notificationHelper: NotificationHelper
) : 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 {

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB