- Commit latest to main

Took 14 minutes
This commit is contained in:
2022-02-06 17:56:36 +00:00
parent e4c8c45e5c
commit d2bffab2e1
26 changed files with 498 additions and 112 deletions

View File

@@ -24,7 +24,7 @@ import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
import kotlin.properties.Delegates
abstract class BaseFragment : Fragment(), KodeinAware {
abstract class BaseFragment() : Fragment(), KodeinAware {
override val kodein by kodein()
val factory by instance<ApplicationViewModelFactory>()

View File

@@ -7,11 +7,9 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
import com.appttude.h_mal.atlas_weather.monoWeather.ui.details.FurtherInfoFragmentArgs
import kotlinx.android.synthetic.main.activity_further_info.*
private const val WEATHER = "param1"
/**
* A simple [Fragment] subclass.
* Use the [FurtherInfoFragment.newInstance] factory method to

View File

@@ -24,7 +24,7 @@ import kotlinx.android.synthetic.main.fragment_home.*
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class HomeFragment : BaseFragment(){
class HomeFragment : BaseFragment() {
private val viewModel by getFragmentViewModel<MainViewModel>()
@@ -49,6 +49,7 @@ class HomeFragment : BaseFragment(){
adapter = recyclerAdapter
}
getPermissionResult(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST){
viewModel.fetchData()
}

View File

@@ -0,0 +1,86 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.widget
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.app.Activity
import android.appwidget.AppWidgetManager.*
import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat.checkSelfPermission
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.utils.displayToast
import kotlinx.android.synthetic.monoWeather.permissions_declaration_dialog.*
const val PERMISSION_CODE = 401
class WidgetLocationPermissionActivity : AppCompatActivity() {
private var mAppWidgetId = INVALID_APPWIDGET_ID
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set the result to CANCELED. This will cause the widget host to cancel
// out of the widget placement if the user presses the back button.
setResult(RESULT_CANCELED)
// Find the widget id from the intent.
intent.extras?.let {
mAppWidgetId = it.getInt(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID)
}
// If this activity was started with an intent without an app widget ID, finish with an error.
if (mAppWidgetId == INVALID_APPWIDGET_ID) {
finish()
return
}
setContentView(R.layout.permissions_declaration_dialog)
submit.setOnClickListener {
if (checkSelfPermission(this, ACCESS_COARSE_LOCATION) != PERMISSION_GRANTED) {
requestPermissions(arrayOf(ACCESS_COARSE_LOCATION), PERMISSION_CODE)
} else {
submitWidget()
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PERMISSION_GRANTED) {
submitWidget()
} else {
displayToast("Location Permission denied")
}
}
}
private fun submitWidget() {
sendUpdateIntent()
finishCurrencyWidgetActivity()
}
private fun finishCurrencyWidgetActivity() {
// Make sure we pass back the original appWidgetId
val resultValue = intent
resultValue.putExtra(EXTRA_APPWIDGET_ID, mAppWidgetId)
setResult(Activity.RESULT_OK, resultValue)
finish()
}
private fun sendUpdateIntent() {
// It is the responsibility of the configuration activity to update the app widget
// Send update broadcast to widget app class
Intent(this@WidgetLocationPermissionActivity,
WidgetLocationPermissionActivity::class.java
).apply {
action = ACTION_APPWIDGET_UPDATE
// Put current app widget ID into extras and send broadcast
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(mAppWidgetId))
sendBroadcast(this)
}
}
}

View File

@@ -9,7 +9,6 @@ import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.monoWeather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.monoWeather.ui.WorldItemFragmentDirections
import com.appttude.h_mal.atlas_weather.monoWeather.ui.world.WorldFragmentDirections.actionWorldFragmentToWorldItemFragment
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel

View File

@@ -35,7 +35,6 @@ class MyWidgetRemoteViewsFactory(
override fun getViewAt(i: Int): RemoteViews {
val rv = RemoteViews(context.packageName, R.layout.widget_item)
if (list.isNullOrEmpty()) return rv
list?.get(i)?.let {
@@ -57,4 +56,5 @@ class MyWidgetRemoteViewsFactory(
override fun getItemId(i: Int): Long = i.toLong()
override fun hasStableIds(): Boolean = true
}

View File

@@ -4,6 +4,7 @@ import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.appttude.h_mal.atlas_weather.monoWeather.widget.WidgetJobServiceIntent.Companion.enqueueWork
/**

View File

@@ -1,19 +1,20 @@
package com.appttude.h_mal.atlas_weather.monoWeather.widget
import android.Manifest
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.net.Uri
import android.os.PowerManager
import android.widget.RemoteViews
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.checkSelfPermission
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.widget.WidgetData
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.monoWeather.ui.MainActivity
import com.appttude.h_mal.atlas_weather.utils.isInternetAvailable
import com.appttude.h_mal.atlas_weather.utils.tryOrNullSuspended
@@ -34,11 +35,7 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
// 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.
val pm = getSystemService(POWER_SERVICE) as PowerManager
val isScreenOn = pm.isInteractive
// If screen is on then update widget or do nothing
if (isScreenOn) executeWidgetUpdate()
executeWidgetUpdate()
}
private fun executeWidgetUpdate(){
@@ -48,23 +45,45 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
val thisAppWidget = ComponentName(packageName, NewAppWidget::class.java.name)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
// Check if we have an active connection and permissions granted
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED || !isInternetAvailable(this.applicationContext)) {
for (appWidgetId in appWidgetIds) {
setEmptyView(this, appWidgetManager, appWidgetId)
}
}else{
CoroutineScope(Dispatchers.IO).launch {
val result = getWidgetWeather()
validateOperation()?.let {
if (it) updateWidget(appWidgetIds, appWidgetManager)
else updateErrorWidget(appWidgetIds, appWidgetManager)
}
}
for (appWidgetId in appWidgetIds) {
bindView(this@WidgetJobServiceIntent, appWidgetManager, appWidgetId, result)
}
private fun updateWidget(appWidgetIds: IntArray, appWidgetManager: AppWidgetManager){
CoroutineScope(Dispatchers.IO).launch {
val result = getWidgetWeather()
for (appWidgetId in appWidgetIds) {
bindView(this@WidgetJobServiceIntent, appWidgetManager, appWidgetId, result)
}
}
}
private fun updateErrorWidget(appWidgetIds: IntArray, appWidgetManager: AppWidgetManager){
for (appWidgetId in appWidgetIds) {
setEmptyView(this, appWidgetManager, appWidgetId)
}
}
private fun validateOperation(): Boolean? {
val pm = getSystemService(POWER_SERVICE) as PowerManager
val isScreenOn = pm.isInteractive
val locationGranted =
checkSelfPermission(this, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
val internetAvailable = isInternetAvailable(this.applicationContext)
// no location return false
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
@@ -76,18 +95,17 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
}
@SuppressLint("MissingPermission")
suspend fun getWidgetWeather(): WidgetData? {
suspend fun getWidgetWeather(): WidgetWeatherCollection? {
return tryOrNullSuspended {
helper.fetchData()
helper.getWidgetWeather()
helper.getWidgetWeatherCollection()
}
}
private fun setEmptyView(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
try {
val error = if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
val error = if (checkSelfPermission(context, ACCESS_COARSE_LOCATION)
!= PERMISSION_GRANTED) {
"No Permission"
} else if (!isInternetAvailable(context.applicationContext)) {
"No Connection"
@@ -100,7 +118,6 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun bindEmptyView(
@@ -128,18 +145,19 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int,
weather: WidgetData?) {
collection: WidgetWeatherCollection?) {
val views = createRemoteView(context, R.layout.weather_app_widget)
setLastUpdated(views)
views.setInt(R.id.whole_widget_view, "setBackgroundColor", helper.getWidgetBackground())
val clickingUpdatePendingIntent = createUpdatePendingIntent(NewAppWidget::class.java, context, appWidgetId)
val forecastListIntent = createForecastListIntent(context, appWidgetId)
if (weather != null) {
if (collection != null) {
val clickPendingIntentTemplate =
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)
@@ -151,7 +169,8 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
setOnClickPendingIntent(R.id.widget_current_icon, clickingUpdatePendingIntent)
setOnClickPendingIntent(R.id.widget_current_location, clickingUpdatePendingIntent)
setRemoteAdapter(R.id.widget_listview, forecastListIntent)
loadCells(appWidgetId, views, collection.forecast)
// setRemoteAdapter(R.id.widget_listview, forecastListIntent)
}
// Instruct the widget manager to update the widget
@@ -163,6 +182,22 @@ class WidgetJobServiceIntent : BaseWidgetServiceIntentClass() {
}
private fun loadCells(appWidgetId: Int, remoteViews: RemoteViews, weather: List<InnerWidgetCellData>){
(0..4).forEach { i ->
val dayId: Int = resources.getIdentifier("widget_item_day_$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 it = weather[i]
remoteViews.setTextViewText(dayId, it.date)
remoteViews.setTextViewText(tempId, it.highTemp)
CoroutineScope(Dispatchers.Main).launch {
Picasso.get().load(it.icon).into(remoteViews, imageId, intArrayOf(appWidgetId))
}
}
}
private fun setLastUpdated(views: RemoteViews){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val current = LocalDateTime.now()