mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
Wake screen widget (#8)
* - Update widget validation * - Cleaned widget folder and code
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
|||||||
/local.properties
|
/local.properties
|
||||||
/.idea/workspace.xml
|
/.idea/workspace.xml
|
||||||
/.idea/libraries
|
/.idea/libraries
|
||||||
|
/.idea/caches
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
|||||||
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
@@ -29,7 +29,7 @@ class ServicesHelper(
|
|||||||
private val repository: Repository,
|
private val repository: Repository,
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val locationProvider: LocationProvider
|
private val locationProvider: LocationProvider
|
||||||
){
|
) {
|
||||||
|
|
||||||
@RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
|
@RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||||
suspend fun fetchData(): Boolean {
|
suspend fun fetchData(): Boolean {
|
||||||
@@ -60,13 +60,14 @@ class ServicesHelper(
|
|||||||
suspend fun getWidgetWeather(): WidgetData? {
|
suspend fun getWidgetWeather(): WidgetData? {
|
||||||
return try {
|
return try {
|
||||||
val result = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
val result = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
||||||
|
val epoc = System.currentTimeMillis()
|
||||||
|
|
||||||
result.weather.let {
|
result.weather.let {
|
||||||
val bitmap = it.current?.icon
|
val bitmap = it.current?.icon
|
||||||
val location = locationProvider.getLocationNameFromLatLong(it.lat, it.lon)
|
val location = locationProvider.getLocationNameFromLatLong(it.lat, it.lon)
|
||||||
val temp = it.current?.temp?.toInt().toString()
|
val temp = it.current?.temp?.toInt().toString()
|
||||||
|
|
||||||
WidgetData(location, bitmap, temp)
|
WidgetData(location, bitmap, temp, epoc)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
@@ -80,7 +81,7 @@ class ServicesHelper(
|
|||||||
|
|
||||||
result.weather.daily?.drop(1)?.dropLast(2)?.forEach { dailyWeather ->
|
result.weather.daily?.drop(1)?.dropLast(2)?.forEach { dailyWeather ->
|
||||||
val day = dailyWeather.dt?.toSmallDayName()
|
val day = dailyWeather.dt?.toSmallDayName()
|
||||||
val bitmap = withContext(Dispatchers.Main) {
|
val bitmap = withContext(Dispatchers.Main) {
|
||||||
getBitmapFromUrl(dailyWeather.icon)
|
getBitmapFromUrl(dailyWeather.icon)
|
||||||
}
|
}
|
||||||
val temp = dailyWeather.max?.toInt().toString()
|
val temp = dailyWeather.max?.toInt().toString()
|
||||||
@@ -102,15 +103,16 @@ class ServicesHelper(
|
|||||||
val bitmap = it.current?.icon
|
val bitmap = it.current?.icon
|
||||||
val location = locationProvider.getLocationNameFromLatLong(it.lat, it.lon)
|
val location = locationProvider.getLocationNameFromLatLong(it.lat, it.lon)
|
||||||
val temp = it.current?.temp?.toInt().toString()
|
val temp = it.current?.temp?.toInt().toString()
|
||||||
|
val epoc = System.currentTimeMillis()
|
||||||
|
|
||||||
WidgetData(location, bitmap, temp)
|
WidgetData(location, bitmap, temp, epoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
val list = mutableListOf<InnerWidgetCellData>()
|
val list = mutableListOf<InnerWidgetCellData>()
|
||||||
|
|
||||||
result.weather.daily?.drop(1)?.dropLast(2)?.forEach { dailyWeather ->
|
result.weather.daily?.drop(1)?.dropLast(2)?.forEach { dailyWeather ->
|
||||||
val day = dailyWeather.dt?.toSmallDayName()
|
val day = dailyWeather.dt?.toSmallDayName()
|
||||||
val icon = dailyWeather.icon
|
val icon = dailyWeather.icon
|
||||||
val temp = dailyWeather.max?.toInt().toString()
|
val temp = dailyWeather.max?.toInt().toString()
|
||||||
|
|
||||||
val item = InnerWidgetCellData(day, icon, temp)
|
val item = InnerWidgetCellData(day, icon, temp)
|
||||||
@@ -126,25 +128,24 @@ class ServicesHelper(
|
|||||||
|
|
||||||
private suspend fun getBitmapFromUrl(imageAddress: String?): Bitmap? {
|
private suspend fun getBitmapFromUrl(imageAddress: String?): Bitmap? {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
Picasso.get().load(imageAddress).into(object : Target {
|
Picasso.get().load(imageAddress).into(object : Target {
|
||||||
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
|
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
|
||||||
cont.resume(bitmap)
|
cont.resume(bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBitmapFailed(e: Exception?, d: Drawable?) {
|
override fun onBitmapFailed(e: Exception?, d: Drawable?) {
|
||||||
cont.resume(null)
|
cont.resume(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}
|
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getWidgetBackground(): Int {
|
fun getWidgetBackground(): Int {
|
||||||
return if (settingsRepository.isBlackBackground()) {
|
return if (settingsRepository.isBlackBackground())
|
||||||
Color.BLACK
|
Color.BLACK
|
||||||
} else {
|
else
|
||||||
Color.TRANSPARENT
|
Color.TRANSPARENT
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,8 @@ import android.graphics.Bitmap
|
|||||||
data class WidgetData(
|
data class WidgetData(
|
||||||
val location: String?,
|
val location: String?,
|
||||||
val icon: String?,
|
val icon: String?,
|
||||||
val currentTemp: String?
|
val currentTemp: String?,
|
||||||
|
val timeStamp: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
data class InnerWidgetData(
|
data class InnerWidgetData(
|
||||||
|
|||||||
@@ -133,6 +133,7 @@
|
|||||||
android:rowCount="1">
|
android:rowCount="1">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/widget_item_0"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_columnWeight="1"
|
android:layout_columnWeight="1"
|
||||||
@@ -165,6 +166,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/widget_item_1"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_columnWeight="1"
|
android:layout_columnWeight="1"
|
||||||
@@ -197,6 +199,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/widget_item_2"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_columnWeight="1"
|
android:layout_columnWeight="1"
|
||||||
@@ -229,6 +232,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/widget_item_3"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_columnWeight="1"
|
android:layout_columnWeight="1"
|
||||||
@@ -260,6 +264,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/widget_item_4"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_columnWeight="1"
|
android:layout_columnWeight="1"
|
||||||
|
|||||||
@@ -5,42 +5,48 @@ import android.app.PendingIntent
|
|||||||
import android.app.TaskStackBuilder
|
import android.app.TaskStackBuilder
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.appwidget.AppWidgetProvider
|
import android.appwidget.AppWidgetProvider
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
|
import androidx.annotation.IdRes
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.core.app.JobIntentService
|
import androidx.core.app.JobIntentService
|
||||||
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
|
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
|
||||||
|
import com.squareup.picasso.Picasso
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.KodeinAware
|
import org.kodein.di.KodeinAware
|
||||||
import org.kodein.di.LateInitKodein
|
import org.kodein.di.LateInitKodein
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
abstract class BaseWidgetServiceIntentClass : JobIntentService(){
|
abstract class BaseWidgetServiceIntentClass<T: AppWidgetProvider> : JobIntentService(){
|
||||||
|
|
||||||
private val kodein = LateInitKodein()
|
lateinit var appWidgetManager: AppWidgetManager
|
||||||
val helper: ServicesHelper by kodein.instance()
|
lateinit var appWidgetIds: IntArray
|
||||||
|
|
||||||
fun setKodein(context: Context){
|
fun initBaseWidget(componentName: ComponentName){
|
||||||
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
appWidgetManager = AppWidgetManager.getInstance(baseContext)
|
||||||
|
appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRemoteView(context: Context, @LayoutRes id: Int): RemoteViews {
|
fun createRemoteView(@LayoutRes id: Int): RemoteViews {
|
||||||
return RemoteViews(context.packageName, id)
|
return RemoteViews(packageName, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create pending intent commonly used for 'click to update' features
|
// Create pending intent commonly used for 'click to update' features
|
||||||
fun <T: AppWidgetProvider> createUpdatePendingIntent(
|
fun createUpdatePendingIntent(
|
||||||
appWidgetProvider: Class<T>,
|
appWidgetProvider: Class<T>,
|
||||||
context: Context,
|
|
||||||
appWidgetId: Int
|
appWidgetId: Int
|
||||||
): PendingIntent? {
|
): PendingIntent? {
|
||||||
val seconds = (System.currentTimeMillis() / 1000L).toInt()
|
val seconds = (System.currentTimeMillis() / 1000L).toInt()
|
||||||
val intentUpdate = Intent(context.applicationContext, appWidgetProvider)
|
val intentUpdate = Intent(applicationContext, appWidgetProvider)
|
||||||
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||||
val idArray = intArrayOf(appWidgetId)
|
val idArray = intArrayOf(appWidgetId)
|
||||||
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
||||||
return PendingIntent.getBroadcast(
|
return PendingIntent.getBroadcast(
|
||||||
context, seconds, intentUpdate,
|
this, seconds, intentUpdate,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,11 +54,26 @@ abstract class BaseWidgetServiceIntentClass : JobIntentService(){
|
|||||||
* create a pending intent used to navigate to activity:
|
* create a pending intent used to navigate to activity:
|
||||||
* @param activityClass
|
* @param activityClass
|
||||||
*/
|
*/
|
||||||
fun <T: Activity> createClickingPendingIntent(context: Context, activityClass: Class<T>): PendingIntent {
|
fun <T: Activity> createClickingPendingIntent(activityClass: Class<T>): PendingIntent {
|
||||||
val clickIntentTemplate = Intent(context, activityClass)
|
val clickIntentTemplate = Intent(this, activityClass)
|
||||||
|
|
||||||
return TaskStackBuilder.create(context)
|
return TaskStackBuilder.create(this)
|
||||||
.addNextIntentWithParentStack(clickIntentTemplate)
|
.addNextIntentWithParentStack(clickIntentTemplate)
|
||||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setImageView(
|
||||||
|
path: String?,
|
||||||
|
views: RemoteViews,
|
||||||
|
@IdRes viewId: Int,
|
||||||
|
appWidgetId: Int
|
||||||
|
){
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
Picasso.get().load(path).into(views, viewId, intArrayOf(appWidgetId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun bindView(widgetId: Int, views: RemoteViews, data: Any?) {}
|
||||||
|
open fun bindEmptyView(widgetId: Int, views: RemoteViews, data: Any?) {}
|
||||||
|
open fun bindErrorView(widgetId: Int, views: RemoteViews, data: Any?) {}
|
||||||
}
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.monoWeather.widget
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.widget.RemoteViews
|
|
||||||
import android.widget.RemoteViewsService.RemoteViewsFactory
|
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
|
||||||
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
|
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetData
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.kodein.di.KodeinAware
|
|
||||||
import org.kodein.di.android.kodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
|
|
||||||
class MyWidgetRemoteViewsFactory(
|
|
||||||
private val context: Context,
|
|
||||||
val intent: Intent
|
|
||||||
) : RemoteViewsFactory, KodeinAware{
|
|
||||||
|
|
||||||
override val kodein by kodein(context)
|
|
||||||
private val helper : ServicesHelper by kodein.instance()
|
|
||||||
|
|
||||||
private var list: List<InnerWidgetData>? = null
|
|
||||||
|
|
||||||
override fun onCreate() {}
|
|
||||||
override fun onDataSetChanged() {
|
|
||||||
runBlocking {
|
|
||||||
list = helper.getWidgetInnerWeather()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onDestroy() {}
|
|
||||||
|
|
||||||
override fun getCount(): Int = list?.size ?: 5
|
|
||||||
|
|
||||||
override fun getViewAt(i: Int): RemoteViews {
|
|
||||||
val rv = RemoteViews(context.packageName, R.layout.widget_item)
|
|
||||||
if (list.isNullOrEmpty()) return rv
|
|
||||||
|
|
||||||
list?.get(i)?.let {
|
|
||||||
rv.setTextViewText(R.id.widget_item_day, it.date)
|
|
||||||
rv.setImageViewBitmap(R.id.widget_item_image, it.icon)
|
|
||||||
rv.setTextViewText(R.id.widget_item_temp_high, it.highTemp)
|
|
||||||
rv.setOnClickFillInIntent(R.id.widget_item_layout, intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLoadingView(): RemoteViews {
|
|
||||||
return RemoteViews(context.packageName, R.layout.widget_item_loading)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getViewTypeCount(): Int = 2
|
|
||||||
|
|
||||||
override fun getItemId(i: Int): Long = i.toLong()
|
|
||||||
|
|
||||||
override fun hasStableIds(): Boolean = true
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -3,187 +3,178 @@ package com.appttude.h_mal.atlas_weather.monoWeather.widget
|
|||||||
import android.Manifest.permission.ACCESS_COARSE_LOCATION
|
import android.Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.appwidget.AppWidgetManager
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||||
import android.net.Uri
|
import android.icu.text.SimpleDateFormat
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
|
import android.os.Build
|
||||||
import androidx.core.app.ActivityCompat.checkSelfPermission
|
import androidx.core.app.ActivityCompat.checkSelfPermission
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetCellData
|
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetCellData
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.WidgetWeatherCollection
|
import com.appttude.h_mal.atlas_weather.model.widget.WidgetWeatherCollection
|
||||||
import com.appttude.h_mal.atlas_weather.monoWeather.ui.MainActivity
|
import com.appttude.h_mal.atlas_weather.monoWeather.ui.MainActivity
|
||||||
|
import com.appttude.h_mal.atlas_weather.monoWeather.widget.WidgetState.*
|
||||||
|
import com.appttude.h_mal.atlas_weather.monoWeather.widget.WidgetState.Companion.getWidgetState
|
||||||
import com.appttude.h_mal.atlas_weather.utils.isInternetAvailable
|
import com.appttude.h_mal.atlas_weather.utils.isInternetAvailable
|
||||||
import com.appttude.h_mal.atlas_weather.utils.tryOrNullSuspended
|
import com.appttude.h_mal.atlas_weather.utils.tryOrNullSuspended
|
||||||
import com.squareup.picasso.Picasso
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.time.LocalDateTime
|
import org.kodein.di.KodeinAware
|
||||||
import java.time.format.DateTimeFormatter
|
import org.kodein.di.LateInitKodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example implementation of a JobIntentService.
|
* Implementation of a JobIntentService used for home screen widget
|
||||||
*/
|
*/
|
||||||
class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
|
const val HALF_DAY = 43200000L
|
||||||
|
class WidgetJobServiceIntent : BaseWidgetServiceIntentClass<NewAppWidget>() {
|
||||||
|
|
||||||
|
private val kodein = LateInitKodein()
|
||||||
|
private val helper: ServicesHelper by kodein.instance()
|
||||||
|
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
// We have received work to do. The system or framework is already
|
// We have received work to do. The system or framework is already
|
||||||
// holding a wake lock for us at this point, so we can just go.
|
// holding a wake lock for us at this point, so we can just go.
|
||||||
|
kodein.baseKodein = (applicationContext as KodeinAware).kodein
|
||||||
executeWidgetUpdate()
|
executeWidgetUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeWidgetUpdate(){
|
private fun executeWidgetUpdate() {
|
||||||
setKodein(this)
|
val componentName = ComponentName(this, NewAppWidget::class.java)
|
||||||
|
initBaseWidget(componentName)
|
||||||
|
|
||||||
val appWidgetManager = AppWidgetManager.getInstance(this)
|
initiateWidgetUpdate(getCurrentWidgetState())
|
||||||
val thisAppWidget = ComponentName(packageName, NewAppWidget::class.java.name)
|
}
|
||||||
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
|
|
||||||
|
|
||||||
validateOperation()?.let {
|
private fun initiateWidgetUpdate(state: WidgetState) {
|
||||||
if (it) updateWidget(appWidgetIds, appWidgetManager)
|
when (state) {
|
||||||
else updateErrorWidget(appWidgetIds, appWidgetManager)
|
NO_LOCATION, SCREEN_ON_CONNECTION_UNAVAILABLE -> updateErrorWidget(state)
|
||||||
|
SCREEN_ON_CONNECTION_AVAILABLE -> updateWidget(false)
|
||||||
|
SCREEN_OFF_CONNECTION_AVAILABLE -> updateWidget(true)
|
||||||
|
SCREEN_OFF_CONNECTION_UNAVAILABLE -> return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateWidget(appWidgetIds: IntArray, appWidgetManager: AppWidgetManager){
|
private fun updateWidget(fromStorage: Boolean) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val result = getWidgetWeather()
|
val result = getWidgetWeather(fromStorage)
|
||||||
|
appWidgetIds.forEach { id -> setupView(id, result) }
|
||||||
for (appWidgetId in appWidgetIds) {
|
|
||||||
bindView(this@WidgetJobServiceIntent, appWidgetManager, appWidgetId, result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateErrorWidget(appWidgetIds: IntArray, appWidgetManager: AppWidgetManager){
|
private fun updateErrorWidget(state: WidgetState) {
|
||||||
for (appWidgetId in appWidgetIds) {
|
appWidgetIds.forEach { id -> setEmptyView(id, state) }
|
||||||
setEmptyView(this, appWidgetManager, appWidgetId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateOperation(): Boolean? {
|
private fun getCurrentWidgetState(): WidgetState {
|
||||||
val pm = getSystemService(POWER_SERVICE) as PowerManager
|
val pm = getSystemService(POWER_SERVICE) as PowerManager
|
||||||
val isScreenOn = pm.isInteractive
|
val isScreenOn = pm.isInteractive
|
||||||
val locationGranted =
|
val locationGranted =
|
||||||
checkSelfPermission(this, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
|
checkSelfPermission(this, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
|
||||||
val internetAvailable = isInternetAvailable(this.applicationContext)
|
val internetAvailable = isInternetAvailable(this.applicationContext)
|
||||||
|
|
||||||
// no location return false
|
return getWidgetState(locationGranted, isScreenOn, internetAvailable)
|
||||||
if (!locationGranted) return false
|
|
||||||
// internet is available lets go
|
|
||||||
if (internetAvailable) return true
|
|
||||||
// screen is off and no connection, do nothing
|
|
||||||
if (!isScreenOn && !internetAvailable) return null
|
|
||||||
|
|
||||||
return if (isScreenOn && !internetAvailable) false else null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createForecastListIntent(
|
|
||||||
context: Context,
|
|
||||||
appWidgetId: Int
|
|
||||||
): Intent {
|
|
||||||
return Intent(context, WidgetRemoteViewsService::class.java).apply {
|
|
||||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
|
|
||||||
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
suspend fun getWidgetWeather(): WidgetWeatherCollection? {
|
suspend fun getWidgetWeather(storageOnly: Boolean): WidgetWeatherCollection? {
|
||||||
return tryOrNullSuspended {
|
return tryOrNullSuspended {
|
||||||
helper.fetchData()
|
if (!storageOnly) helper.fetchData()
|
||||||
helper.getWidgetWeatherCollection()
|
helper.getWidgetWeatherCollection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setEmptyView(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
|
private fun setEmptyView(appWidgetId: Int, state: WidgetState) {
|
||||||
try {
|
val error = when (state) {
|
||||||
val error = if (checkSelfPermission(context, ACCESS_COARSE_LOCATION)
|
NO_LOCATION -> "No Location Permission"
|
||||||
!= PERMISSION_GRANTED) {
|
SCREEN_ON_CONNECTION_UNAVAILABLE -> "No network available"
|
||||||
"No Permission"
|
else -> "No data"
|
||||||
} else if (!isInternetAvailable(context.applicationContext)) {
|
|
||||||
"No Connection"
|
|
||||||
} else {
|
|
||||||
"No Data"
|
|
||||||
}
|
|
||||||
val updatePendingIntent = createUpdatePendingIntent(NewAppWidget::class.java, context, appWidgetId)
|
|
||||||
val views = createRemoteView(context, R.layout.weather_app_widget)
|
|
||||||
bindEmptyView(appWidgetManager, appWidgetId, views, updatePendingIntent, error)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val views = createRemoteView(R.layout.weather_app_widget)
|
||||||
|
bindErrorView(appWidgetId, views, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindEmptyView(
|
private fun setupView(
|
||||||
appWidgetManager: AppWidgetManager,
|
|
||||||
appWidgetId: Int,
|
appWidgetId: Int,
|
||||||
views: RemoteViews,
|
collection: WidgetWeatherCollection?
|
||||||
clickingUpdateIntent: PendingIntent?,
|
|
||||||
warning: String
|
|
||||||
) {
|
) {
|
||||||
setLastUpdated(views)
|
val views = createRemoteView(R.layout.weather_app_widget)
|
||||||
views.setTextViewText(R.id.widget_current_location, warning)
|
setLastUpdated(views, collection?.widgetData?.timeStamp)
|
||||||
views.setImageViewResource(R.id.widget_current_icon, R.drawable.ic_baseline_cloud_off_24)
|
|
||||||
views.setImageViewResource(R.id.location_icon, 0)
|
|
||||||
|
|
||||||
views.setTextViewText(R.id.widget_main_temp, "")
|
|
||||||
views.setTextViewText(R.id.widget_feel_temp, "")
|
|
||||||
|
|
||||||
views.setOnClickPendingIntent(R.id.widget_current_icon, clickingUpdateIntent)
|
|
||||||
views.setOnClickPendingIntent(R.id.widget_current_location, clickingUpdateIntent)
|
|
||||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
|
||||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindView(
|
|
||||||
context: Context,
|
|
||||||
appWidgetManager: AppWidgetManager,
|
|
||||||
appWidgetId: Int,
|
|
||||||
collection: WidgetWeatherCollection?) {
|
|
||||||
val views = createRemoteView(context, R.layout.weather_app_widget)
|
|
||||||
setLastUpdated(views)
|
|
||||||
views.setInt(R.id.whole_widget_view, "setBackgroundColor", helper.getWidgetBackground())
|
views.setInt(R.id.whole_widget_view, "setBackgroundColor", helper.getWidgetBackground())
|
||||||
val clickingUpdatePendingIntent = createUpdatePendingIntent(NewAppWidget::class.java, context, appWidgetId)
|
|
||||||
|
|
||||||
if (collection != null) {
|
if (collection != null) {
|
||||||
val clickPendingIntentTemplate =
|
bindView(appWidgetId, views, collection)
|
||||||
createClickingPendingIntent(context, MainActivity::class.java)
|
|
||||||
|
|
||||||
views.apply {
|
|
||||||
val weather = collection.widgetData
|
|
||||||
|
|
||||||
setTextViewText(R.id.widget_main_temp, weather.currentTemp)
|
|
||||||
setTextViewText(R.id.widget_feel_temp, "°C")
|
|
||||||
setTextViewText(R.id.widget_current_location, weather.location)
|
|
||||||
setImageViewResource(R.id.location_icon, R.drawable.location_flag)
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
|
||||||
Picasso.get().load(weather.icon).into(views, R.id.widget_current_icon, intArrayOf(appWidgetId))
|
|
||||||
}
|
|
||||||
setPendingIntentTemplate(R.id.widget_listview, clickPendingIntentTemplate)
|
|
||||||
setOnClickPendingIntent(R.id.widget_current_icon, clickingUpdatePendingIntent)
|
|
||||||
setOnClickPendingIntent(R.id.widget_current_location, clickingUpdatePendingIntent)
|
|
||||||
|
|
||||||
loadCells(appWidgetId, views, collection.forecast)
|
|
||||||
// setRemoteAdapter(R.id.widget_listview, forecastListIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instruct the widget manager to update the widget
|
|
||||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
|
||||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
|
|
||||||
} else {
|
} else {
|
||||||
bindEmptyView(appWidgetManager, appWidgetId, views, clickingUpdatePendingIntent, "No Connection")
|
bindEmptyView(appWidgetId, views, "No weather available")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadCells(appWidgetId: Int, remoteViews: RemoteViews, weather: List<InnerWidgetCellData>){
|
override fun bindErrorView(
|
||||||
|
widgetId: Int,
|
||||||
|
views: RemoteViews,
|
||||||
|
data: Any?
|
||||||
|
) {
|
||||||
|
bindEmptyView(widgetId, views, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindEmptyView(
|
||||||
|
widgetId: Int,
|
||||||
|
views: RemoteViews,
|
||||||
|
data: Any?
|
||||||
|
) {
|
||||||
|
val clickUpdate = createUpdatePendingIntent(NewAppWidget::class.java, widgetId)
|
||||||
|
|
||||||
|
views.apply {
|
||||||
|
setTextViewText(R.id.widget_current_location, data as String)
|
||||||
|
setImageViewResource(R.id.widget_current_icon, R.drawable.ic_baseline_cloud_off_24)
|
||||||
|
setImageViewResource(R.id.location_icon, 0)
|
||||||
|
|
||||||
|
setTextViewText(R.id.widget_main_temp, "")
|
||||||
|
setTextViewText(R.id.widget_feel_temp, "")
|
||||||
|
|
||||||
|
setOnClickPendingIntent(R.id.widget_current_icon, clickUpdate)
|
||||||
|
setOnClickPendingIntent(R.id.widget_current_location, clickUpdate)
|
||||||
|
appWidgetManager.updateAppWidget(widgetId, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindView(widgetId: Int, views: RemoteViews, data: Any?) {
|
||||||
|
val clickUpdate = createUpdatePendingIntent(NewAppWidget::class.java, widgetId)
|
||||||
|
val clickToMain = createClickingPendingIntent(MainActivity::class.java)
|
||||||
|
|
||||||
|
val collection = data as WidgetWeatherCollection
|
||||||
|
val weather = collection.widgetData
|
||||||
|
views.apply {
|
||||||
|
setTextViewText(R.id.widget_main_temp, weather.currentTemp)
|
||||||
|
setTextViewText(R.id.widget_feel_temp, "°C")
|
||||||
|
setTextViewText(R.id.widget_current_location, weather.location)
|
||||||
|
setImageViewResource(R.id.location_icon, R.drawable.location_flag)
|
||||||
|
setImageView(weather.icon, this, R.id.widget_current_icon, widgetId)
|
||||||
|
setOnClickPendingIntent(R.id.widget_current_icon, clickUpdate)
|
||||||
|
setOnClickPendingIntent(R.id.widget_current_location, clickUpdate)
|
||||||
|
|
||||||
|
loadCells(widgetId, this, collection.forecast, clickToMain)
|
||||||
|
// Instruct the widget manager to update the widget
|
||||||
|
appWidgetManager.updateAppWidget(widgetId, views)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadCells(
|
||||||
|
appWidgetId: Int,
|
||||||
|
remoteViews: RemoteViews,
|
||||||
|
weather: List<InnerWidgetCellData>,
|
||||||
|
clickIntent: PendingIntent
|
||||||
|
) {
|
||||||
(0..4).forEach { i ->
|
(0..4).forEach { i ->
|
||||||
|
val containerId: Int = resources.getIdentifier("widget_item_$i", "id", packageName)
|
||||||
val dayId: Int = resources.getIdentifier("widget_item_day_$i", "id", packageName)
|
val dayId: Int = resources.getIdentifier("widget_item_day_$i", "id", packageName)
|
||||||
val imageId: Int = resources.getIdentifier("widget_item_image_$i", "id", packageName)
|
val imageId: Int = resources.getIdentifier("widget_item_image_$i", "id", packageName)
|
||||||
val tempId: Int = resources.getIdentifier("widget_item_temp_high_$i", "id", packageName)
|
val tempId: Int = resources.getIdentifier("widget_item_temp_high_$i", "id", packageName)
|
||||||
@@ -192,18 +183,24 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
|
|||||||
|
|
||||||
remoteViews.setTextViewText(dayId, it.date)
|
remoteViews.setTextViewText(dayId, it.date)
|
||||||
remoteViews.setTextViewText(tempId, it.highTemp)
|
remoteViews.setTextViewText(tempId, it.highTemp)
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
setImageView(it.icon, remoteViews, imageId, appWidgetId)
|
||||||
Picasso.get().load(it.icon).into(remoteViews, imageId, intArrayOf(appWidgetId))
|
remoteViews.setOnClickPendingIntent(containerId, clickIntent)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setLastUpdated(views: RemoteViews){
|
private fun setLastUpdated(views: RemoteViews, timeStamp: Long?) {
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && timeStamp != null) {
|
||||||
val current = LocalDateTime.now()
|
val difference = System.currentTimeMillis().minus(timeStamp)
|
||||||
val formatter = DateTimeFormatter.ofPattern("HH:mm")
|
|
||||||
val formatted = current.format(formatter)
|
val status = if (difference > HALF_DAY) {
|
||||||
views.setTextViewText(R.id.widget_current_status, "last updated: $formatted")
|
"12hrs ago"
|
||||||
|
} else {
|
||||||
|
val date = Date(timeStamp)
|
||||||
|
val sdf = SimpleDateFormat("HH:mm", Locale.getDefault())
|
||||||
|
sdf.format(date)
|
||||||
|
}
|
||||||
|
|
||||||
|
views.setTextViewText(R.id.widget_current_status, "last updated: $status")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.monoWeather.widget
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.widget.RemoteViewsService
|
|
||||||
|
|
||||||
class WidgetRemoteViewsService : RemoteViewsService() {
|
|
||||||
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
|
|
||||||
return MyWidgetRemoteViewsFactory(applicationContext, intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.monoWeather.widget
|
||||||
|
|
||||||
|
enum class WidgetState {
|
||||||
|
NO_LOCATION,
|
||||||
|
SCREEN_ON_CONNECTION_AVAILABLE,
|
||||||
|
SCREEN_ON_CONNECTION_UNAVAILABLE,
|
||||||
|
SCREEN_OFF_CONNECTION_AVAILABLE,
|
||||||
|
SCREEN_OFF_CONNECTION_UNAVAILABLE;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getWidgetState(
|
||||||
|
locationAvailable: Boolean,
|
||||||
|
screenOn: Boolean,
|
||||||
|
connectionAvailable: Boolean
|
||||||
|
): WidgetState {
|
||||||
|
return if (!locationAvailable)
|
||||||
|
NO_LOCATION
|
||||||
|
else if (screenOn && connectionAvailable)
|
||||||
|
SCREEN_ON_CONNECTION_AVAILABLE
|
||||||
|
else if (screenOn && !connectionAvailable)
|
||||||
|
SCREEN_ON_CONNECTION_UNAVAILABLE
|
||||||
|
else if (!screenOn && connectionAvailable)
|
||||||
|
SCREEN_OFF_CONNECTION_AVAILABLE
|
||||||
|
else
|
||||||
|
SCREEN_OFF_CONNECTION_UNAVAILABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.monoWeather.widget
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.app.TaskStackBuilder
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
|
|
||||||
fun <T: Activity> createClickingPendingIntent(context: Context, activityClass: Class<T>): PendingIntent {
|
|
||||||
val clickIntentTemplate = Intent(context, activityClass)
|
|
||||||
|
|
||||||
return TaskStackBuilder.create(context)
|
|
||||||
.addNextIntentWithParentStack(clickIntentTemplate)
|
|
||||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user