mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2026-03-18 15:36:04 +00:00
Refactor flavours (#17)
- Fastlane completed - Circleci config completed - Flavours build completed
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.appttude.h_mal.atlas_weather">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SET_ALARM" />
|
||||
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
|
||||
<application android:networkSecurityConfig="@xml/network_security_config" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.location"
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
package com.appttude.h_mal.atlas_weather.application
|
||||
|
||||
import androidx.room.RoomDatabase
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
||||
import com.appttude.h_mal.atlas_weather.data.network.Api
|
||||
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
|
||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||
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.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
|
||||
|
||||
const val LOCATION_PERMISSION_REQUEST = 505
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
||||
class LocationProviderImpl(
|
||||
val applicationContext: Context
|
||||
private val applicationContext: Context
|
||||
) : LocationProvider, LocationHelper(applicationContext) {
|
||||
private var locationManager =
|
||||
applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.networkUtils
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkInterceptor
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@@ -3,59 +3,61 @@ package com.appttude.h_mal.atlas_weather.model.forecast
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.DailyWeather
|
||||
import com.appttude.h_mal.atlas_weather.utils.parcelableCreator
|
||||
import com.appttude.h_mal.atlas_weather.utils.toDayName
|
||||
import com.appttude.h_mal.atlas_weather.utils.toDayString
|
||||
import com.appttude.h_mal.atlas_weather.utils.toTime
|
||||
|
||||
|
||||
data class Forecast(
|
||||
val date: String?,
|
||||
val day: String?,
|
||||
val condition: String?,
|
||||
val weatherIcon: String?,
|
||||
val mainTemp: String?,
|
||||
val minorTemp: String?,
|
||||
val averageTemp: String?,
|
||||
val windText: String?,
|
||||
val precipitation: String?,
|
||||
val humidity: String?,
|
||||
val uvi: String?,
|
||||
val sunrise: String?,
|
||||
val sunset: String?,
|
||||
val cloud: String?
|
||||
): Parcelable {
|
||||
val date: String?,
|
||||
val day: String?,
|
||||
val condition: String?,
|
||||
val weatherIcon: String?,
|
||||
val mainTemp: String?,
|
||||
val minorTemp: String?,
|
||||
val averageTemp: String?,
|
||||
val windText: String?,
|
||||
val precipitation: String?,
|
||||
val humidity: String?,
|
||||
val uvi: String?,
|
||||
val sunrise: String?,
|
||||
val sunset: String?,
|
||||
val cloud: String?
|
||||
): Parcelable{
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString())
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString()
|
||||
) {
|
||||
}
|
||||
|
||||
constructor(dailyWeather: DailyWeather) : this(
|
||||
dailyWeather.dt?.toDayString(),
|
||||
dailyWeather.dt?.toDayName(),
|
||||
dailyWeather.description,
|
||||
dailyWeather.icon,
|
||||
dailyWeather.max?.toInt().toString(),
|
||||
dailyWeather.min?.toInt().toString(),
|
||||
dailyWeather.average?.toInt().toString(),
|
||||
dailyWeather.windSpeed?.toInt().toString(),
|
||||
(dailyWeather.pop?.times(100))?.toInt().toString(),
|
||||
dailyWeather.humidity?.toString(),
|
||||
dailyWeather.uvi?.toInt().toString(),
|
||||
dailyWeather.sunrise?.toTime(),
|
||||
dailyWeather.sunset?.toTime(),
|
||||
dailyWeather.clouds?.toString()
|
||||
dailyWeather.dt?.toDayString(),
|
||||
dailyWeather.dt?.toDayName(),
|
||||
dailyWeather.description,
|
||||
dailyWeather.icon,
|
||||
dailyWeather.max?.toInt().toString(),
|
||||
dailyWeather.min?.toInt().toString(),
|
||||
dailyWeather.average?.toInt().toString(),
|
||||
dailyWeather.windSpeed?.toInt().toString(),
|
||||
(dailyWeather.pop?.times(100))?.toInt().toString(),
|
||||
dailyWeather.humidity?.toString(),
|
||||
dailyWeather.uvi?.toInt().toString(),
|
||||
dailyWeather.sunrise?.toTime(),
|
||||
dailyWeather.sunset?.toTime(),
|
||||
dailyWeather.clouds?.toString()
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
@@ -72,13 +74,20 @@ data class Forecast(
|
||||
parcel.writeString(uvi)
|
||||
parcel.writeString(sunrise)
|
||||
parcel.writeString(sunset)
|
||||
parcel.writeString(cloud)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object{
|
||||
@JvmField val CREATOR = parcelableCreator(::Forecast)
|
||||
companion object CREATOR : Parcelable.Creator<Forecast> {
|
||||
override fun createFromParcel(parcel: Parcel): Forecast {
|
||||
return Forecast(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Forecast?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,97 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.forecast
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||
|
||||
|
||||
data class WeatherDisplay(
|
||||
val averageTemp: Double?,
|
||||
var unit: String?,
|
||||
var location: String?,
|
||||
val iconURL: String?,
|
||||
val description: String?,
|
||||
val hourly: List<Hour>?,
|
||||
val forecast: List<Forecast>?,
|
||||
val windSpeed: String?,
|
||||
val windDirection: String?,
|
||||
val precipitation: String?,
|
||||
val humidity: String?,
|
||||
val clouds: String?,
|
||||
val lat: Double = 0.00,
|
||||
val lon: Double = 0.00,
|
||||
var displayName: String?
|
||||
){
|
||||
val averageTemp: Double?,
|
||||
var unit: String?,
|
||||
var location: String?,
|
||||
val iconURL: String?,
|
||||
val description: String?,
|
||||
val hourly: List<Hour>?,
|
||||
val forecast: List<Forecast>?,
|
||||
val windSpeed: String?,
|
||||
val windDirection: String?,
|
||||
val precipitation: String?,
|
||||
val humidity: String?,
|
||||
val clouds: String?,
|
||||
val lat: Double = 0.00,
|
||||
val lon: Double = 0.00,
|
||||
var displayName: String?
|
||||
): Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.createTypedArrayList(Hour),
|
||||
parcel.createTypedArrayList(Forecast),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readDouble(),
|
||||
parcel.readDouble(),
|
||||
parcel.readString()
|
||||
) {
|
||||
}
|
||||
|
||||
constructor(entity: EntityItem) : this(
|
||||
entity.weather.current?.temp,
|
||||
entity.weather.temperatureUnit,
|
||||
entity.id,
|
||||
entity.weather.current?.icon,
|
||||
entity.weather.current?.description,
|
||||
entity.weather.hourly,
|
||||
entity.weather.daily?.drop(1)?.map { Forecast(it) },
|
||||
entity.weather.current?.windSpeed?.toString(),
|
||||
entity.weather.current?.windDeg?.toString(),
|
||||
entity.weather.daily?.get(0)?.pop?.times(100)?.toInt()?.toString(),
|
||||
entity.weather.current?.humidity?.toString(),
|
||||
entity.weather.current?.clouds?.toString(),
|
||||
entity.weather.lat,
|
||||
entity.weather.lon,
|
||||
entity.weather.locationString
|
||||
entity.weather.current?.temp,
|
||||
entity.weather.temperatureUnit,
|
||||
entity.id,
|
||||
entity.weather.current?.icon,
|
||||
entity.weather.current?.description,
|
||||
entity.weather.hourly,
|
||||
entity.weather.daily?.drop(1)?.map { Forecast(it) },
|
||||
entity.weather.current?.windSpeed?.toString(),
|
||||
entity.weather.current?.windDeg?.toString(),
|
||||
entity.weather.daily?.get(0)?.pop?.times(100)?.toInt()?.toString(),
|
||||
entity.weather.current?.humidity?.toString(),
|
||||
entity.weather.current?.clouds?.toString(),
|
||||
entity.weather.lat,
|
||||
entity.weather.lon,
|
||||
entity.weather.locationString
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeValue(averageTemp)
|
||||
parcel.writeString(unit)
|
||||
parcel.writeString(location)
|
||||
parcel.writeString(iconURL)
|
||||
parcel.writeString(description)
|
||||
parcel.writeTypedList(hourly)
|
||||
parcel.writeTypedList(forecast)
|
||||
parcel.writeString(windSpeed)
|
||||
parcel.writeString(windDirection)
|
||||
parcel.writeString(precipitation)
|
||||
parcel.writeString(humidity)
|
||||
parcel.writeString(clouds)
|
||||
parcel.writeDouble(lat)
|
||||
parcel.writeDouble(lon)
|
||||
parcel.writeString(displayName)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<WeatherDisplay> {
|
||||
override fun createFromParcel(parcel: Parcel): WeatherDisplay {
|
||||
return WeatherDisplay(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<WeatherDisplay?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,45 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.weather
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Current
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
||||
|
||||
data class Current(
|
||||
val dt: Int? = null,
|
||||
val sunrise: Int? = null,
|
||||
val sunset: Int? = null,
|
||||
val temp: Double? = null,
|
||||
val visibility: Int? = null,
|
||||
val uvi: Double? = null,
|
||||
val pressure: Int? = null,
|
||||
val clouds: Int? = null,
|
||||
val feelsLike: Double? = null,
|
||||
val windDeg: Int? = null,
|
||||
val dewPoint: Double? = null,
|
||||
val icon: String? = null,
|
||||
val description: String? = null,
|
||||
val main: String? = null,
|
||||
val id: Int? = null,
|
||||
val humidity: Int? = null,
|
||||
val windSpeed: Double? = null
|
||||
){
|
||||
val dt: Int? = null,
|
||||
val sunrise: Int? = null,
|
||||
val sunset: Int? = null,
|
||||
val temp: Double? = null,
|
||||
val visibility: Int? = null,
|
||||
val uvi: Double? = null,
|
||||
val pressure: Int? = null,
|
||||
val clouds: Int? = null,
|
||||
val feelsLike: Double? = null,
|
||||
val windDeg: Int? = null,
|
||||
val dewPoint: Double? = null,
|
||||
val icon: String? = null,
|
||||
val description: String? = null,
|
||||
val main: String? = null,
|
||||
val id: Int? = null,
|
||||
val humidity: Int? = null,
|
||||
val windSpeed: Double? = null
|
||||
) {
|
||||
|
||||
constructor(dailyItem: Current): this(
|
||||
dailyItem.dt,
|
||||
dailyItem.sunrise,
|
||||
dailyItem.sunset,
|
||||
dailyItem.temp,
|
||||
dailyItem.visibility,
|
||||
dailyItem.uvi,
|
||||
dailyItem.pressure,
|
||||
dailyItem.clouds,
|
||||
dailyItem.feelsLike,
|
||||
dailyItem.windDeg,
|
||||
dailyItem.dewPoint,
|
||||
dailyItem.weather?.get(0)?.icon?.let { "https://openweathermap.org/img/wn/${it}@4x.png" },
|
||||
dailyItem.weather?.get(0)?.description,
|
||||
dailyItem.weather?.get(0)?.main,
|
||||
dailyItem.weather?.get(0)?.id,
|
||||
dailyItem.humidity,
|
||||
dailyItem.windSpeed
|
||||
)
|
||||
constructor(dailyItem: Current) : this(
|
||||
dailyItem.dt,
|
||||
dailyItem.sunrise,
|
||||
dailyItem.sunset,
|
||||
dailyItem.temp,
|
||||
dailyItem.visibility,
|
||||
dailyItem.uvi,
|
||||
dailyItem.pressure,
|
||||
dailyItem.clouds,
|
||||
dailyItem.feelsLike,
|
||||
dailyItem.windDeg,
|
||||
dailyItem.dewPoint,
|
||||
generateIconUrlString(dailyItem.weather?.getOrNull(0)?.icon),
|
||||
dailyItem.weather?.get(0)?.description,
|
||||
dailyItem.weather?.get(0)?.main,
|
||||
dailyItem.weather?.get(0)?.id,
|
||||
dailyItem.humidity,
|
||||
dailyItem.windSpeed
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.weather
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.DailyItem
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
||||
|
||||
|
||||
data class DailyWeather(
|
||||
@@ -39,7 +40,7 @@ data class DailyWeather(
|
||||
dailyItem.dewPoint,
|
||||
dailyItem.windSpeed,
|
||||
dailyItem.windDeg,
|
||||
dailyItem.weather?.get(0)?.icon?.let { "https://openweathermap.org/img/wn/${it}@4x.png" },
|
||||
generateIconUrlString(dailyItem.weather?.getOrNull(0)?.icon),
|
||||
dailyItem.weather?.get(0)?.description,
|
||||
dailyItem.weather?.get(0)?.main,
|
||||
dailyItem.weather?.get(0)?.id,
|
||||
|
||||
@@ -1,19 +1,47 @@
|
||||
package com.appttude.h_mal.atlas_weather.model.weather
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Hour as ForecastHour
|
||||
|
||||
|
||||
data class Hour(
|
||||
val dt: Int? = null,
|
||||
val temp: Double? = null,
|
||||
val icon: String? = null
|
||||
){
|
||||
constructor(hour: ForecastHour) : this(
|
||||
hour.dt,
|
||||
hour.temp,
|
||||
generateIconUrlString(hour.weather?.getOrNull(0)?.icon)
|
||||
)
|
||||
}
|
||||
val dt: Int? = null,
|
||||
val temp: Double? = null,
|
||||
val icon: String? = null
|
||||
): Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||
parcel.readString()
|
||||
) {
|
||||
}
|
||||
|
||||
constructor(hour: ForecastHour) : this(
|
||||
hour.dt,
|
||||
hour.temp,
|
||||
generateIconUrlString(hour.weather?.getOrNull(0)?.icon)
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeValue(dt)
|
||||
parcel.writeValue(temp)
|
||||
parcel.writeString(icon)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Hour> {
|
||||
override fun createFromParcel(parcel: Parcel): Hour {
|
||||
return Hour(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Hour?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package com.appttude.h_mal.monoWeather.ui
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
||||
import com.appttude.h_mal.atlas_weather.utils.Event
|
||||
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
||||
import com.appttude.h_mal.atlas_weather.utils.hide
|
||||
import com.appttude.h_mal.atlas_weather.utils.show
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.x.kodein
|
||||
import org.kodein.di.generic.instance
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId),
|
||||
KodeinAware {
|
||||
|
||||
override val kodein by kodein()
|
||||
val factory by instance<ApplicationViewModelFactory>()
|
||||
|
||||
inline fun <reified VM : ViewModel> getFragmentViewModel(): Lazy<VM> = viewModels { factory }
|
||||
|
||||
private var shortAnimationDuration by Delegates.notNull<Int>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||
}
|
||||
|
||||
// toggle visibility of progress spinner while async operations are taking place
|
||||
fun progressBarStateObserver(progressBar: View) = Observer<Event<Boolean>> {
|
||||
it.getContentIfNotHandled()?.let { i ->
|
||||
if (i)
|
||||
progressBar.fadeIn()
|
||||
else
|
||||
progressBar.fadeOut()
|
||||
}
|
||||
}
|
||||
|
||||
// display a toast when operation fails
|
||||
fun errorObserver() = Observer<Event<String>> {
|
||||
it.getContentIfNotHandled()?.let { message ->
|
||||
displayToast(message)
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshObserver(refresher: SwipeRefreshLayout) = Observer<Event<Boolean>> {
|
||||
refresher.isRefreshing = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a permission for
|
||||
* @param permission with
|
||||
* @param permissionCode
|
||||
* Callback if is already permission granted
|
||||
* @param permissionGranted
|
||||
*/
|
||||
fun getPermissionResult(
|
||||
permission: String,
|
||||
permissionCode: Int,
|
||||
permissionGranted: () -> Unit
|
||||
) {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
permission
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
requestPermissions(arrayOf(permission), permissionCode)
|
||||
return
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
permissionGranted.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.fadeIn() {
|
||||
apply {
|
||||
// Set the content view to 0% opacity but visible, so that it is visible
|
||||
// (but fully transparent) during the animation.
|
||||
alpha = 0f
|
||||
hide()
|
||||
|
||||
// Animate the content view to 100% opacity, and clear any animation
|
||||
// listener set on the view.
|
||||
animate()
|
||||
.alpha(1f)
|
||||
.setDuration(shortAnimationDuration.toLong())
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.fadeOut() {
|
||||
apply {
|
||||
// Set the content view to 0% opacity but visible, so that it is visible
|
||||
// (but fully transparent) during the animation.
|
||||
alpha = 1f
|
||||
show()
|
||||
|
||||
// Animate the content view to 100% opacity, and clear any animation
|
||||
// listener set on the view.
|
||||
animate()
|
||||
.alpha(0f)
|
||||
.setDuration(shortAnimationDuration.toLong())
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
hide()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String?>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == LOCATION_PERMISSION_REQUEST) {
|
||||
if (grantResults.isNotEmpty() &&
|
||||
grantResults[0] == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
permissionsGranted()
|
||||
displayToast("Permission granted")
|
||||
} else {
|
||||
permissionsRefused()
|
||||
displayToast("Permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun permissionsGranted() {}
|
||||
open fun permissionsRefused() {}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import kotlinx.android.synthetic.main.activity_main_navigation.*
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
lateinit var navHost: NavHostFragment
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main_navigation)
|
||||
|
||||
val navView: BottomNavigationView = findViewById(R.id.nav_view)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
navHost = supportFragmentManager
|
||||
.findFragmentById(R.id.container) as NavHostFragment
|
||||
val navController = navHost.navController
|
||||
navController.setGraph(R.navigation.main_navigation)
|
||||
|
||||
setupBottomBar(navView, navController)
|
||||
}
|
||||
|
||||
private fun setupBottomBar(navView: BottomNavigationView, navController: NavController) {
|
||||
val appBarConfiguration = AppBarConfiguration(tabs)
|
||||
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
navView.setupWithNavController(navController)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
// Handle action bar item clicks here. The action bar will
|
||||
// automatically handle clicks on the Home/Up button, so long
|
||||
// as you specify a parent activity in AndroidManifest.xml.
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ package com.appttude.h_mal.atlas_weather.utils
|
||||
fun generateIconUrlString(icon: String?): String?{
|
||||
return icon?.let {
|
||||
StringBuilder()
|
||||
.append("https://openweathermap.org/img/wn/")
|
||||
.append("http://openweathermap.org/img/wn/")
|
||||
.append(it)
|
||||
.append("@4x.png")
|
||||
.append("@2x.png")
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.appttude.h_mal.atlas_weather.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -10,7 +11,7 @@ import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.bumptech.glide.Glide
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
fun View.show() {
|
||||
this.visibility = View.VISIBLE
|
||||
@@ -33,16 +34,10 @@ fun ViewGroup.generateView(layoutId: Int): View = LayoutInflater
|
||||
.inflate(layoutId, this, false)
|
||||
|
||||
fun ImageView.loadImage(url: String?){
|
||||
val c = Glide.with(this)
|
||||
.load(url)
|
||||
viewTreeObserver.addOnPreDrawListener {
|
||||
c.override(width, height)
|
||||
true
|
||||
}
|
||||
c.placeholder(R.drawable.ic_baseline_cloud_queue_24)
|
||||
.error(R.drawable.ic_baseline_cloud_off_24)
|
||||
.fitCenter()
|
||||
.into(this)
|
||||
Picasso.get().load(url)
|
||||
.placeholder(R.drawable.ic_baseline_cloud_queue_24)
|
||||
.error(R.drawable.ic_baseline_cloud_off_24)
|
||||
.into(this)
|
||||
}
|
||||
|
||||
fun Fragment.hideKeyboard() {
|
||||
|
||||
@@ -5,9 +5,10 @@ import androidx.lifecycle.ViewModelProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||
|
||||
|
||||
class ApplicationViewModelFactory(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: RepositoryImpl
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: RepositoryImpl
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
||||
@@ -15,8 +15,8 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainViewModel(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: Repository
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: Repository
|
||||
): WeatherViewModel(){
|
||||
|
||||
val weatherLiveData = MutableLiveData<WeatherDisplay>()
|
||||
|
||||
@@ -16,8 +16,8 @@ import java.io.IOException
|
||||
|
||||
const val ALL_LOADED = "all_loaded"
|
||||
class WorldViewModel(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: Repository
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: Repository
|
||||
) : WeatherViewModel() {
|
||||
|
||||
val weatherLiveData = MutableLiveData<List<WeatherDisplay>>()
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.appttude.h_mal.atlas_weather.widget
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||
import android.app.TaskStackBuilder
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.widget.RemoteViews
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseWidgetServiceIntentClass<T : AppWidgetProvider> : JobIntentService() {
|
||||
|
||||
lateinit var appWidgetManager: AppWidgetManager
|
||||
lateinit var appWidgetIds: IntArray
|
||||
|
||||
fun initBaseWidget(componentName: ComponentName) {
|
||||
appWidgetManager = AppWidgetManager.getInstance(baseContext)
|
||||
appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
|
||||
}
|
||||
|
||||
fun createRemoteView(@LayoutRes id: Int): RemoteViews {
|
||||
return RemoteViews(packageName, id)
|
||||
}
|
||||
|
||||
// Create pending intent commonly used for 'click to update' features
|
||||
fun createUpdatePendingIntent(
|
||||
appWidgetProvider: Class<T>,
|
||||
appWidgetId: Int
|
||||
): PendingIntent? {
|
||||
val seconds = (System.currentTimeMillis() / 1000L).toInt()
|
||||
val intentUpdate = Intent(applicationContext, appWidgetProvider)
|
||||
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
val idArray = intArrayOf(appWidgetId)
|
||||
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
||||
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PendingIntent.getBroadcast(this, seconds, intentUpdate, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
} else {
|
||||
PendingIntent.getBroadcast(this, seconds, intentUpdate, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a pending intent used to navigate to activity:
|
||||
* @param activityClass
|
||||
*/
|
||||
fun <T : Activity> createClickingPendingIntent(activityClass: Class<T>): PendingIntent {
|
||||
val clickIntentTemplate = Intent(this, activityClass)
|
||||
|
||||
return TaskStackBuilder.create(this)
|
||||
.addNextIntentWithParentStack(clickIntentTemplate)
|
||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
|
||||
}
|
||||
|
||||
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?) {}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.appttude.h_mal.atlas_weather.widget
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.appttude.h_mal.atlas_weather.widget.WidgetJobServiceIntent.Companion.enqueueWork
|
||||
|
||||
/**
|
||||
* Implementation of App Widget functionality.
|
||||
*/
|
||||
class NewAppWidget : AppWidgetProvider() {
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
|
||||
loadWidget(context)
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
super.onEnabled(context)
|
||||
|
||||
loadWidget(context)
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) { }
|
||||
|
||||
private fun loadWidget(context: Context){
|
||||
val mIntent = Intent(context, WidgetJobServiceIntent::class.java)
|
||||
enqueueWork(context, mIntent)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package com.appttude.h_mal.atlas_weather.widget
|
||||
|
||||
import android.Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.os.PowerManager
|
||||
import android.widget.RemoteViews
|
||||
import android.os.Build
|
||||
import androidx.core.app.ActivityCompat.checkSelfPermission
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.widget.WidgetState.*
|
||||
import com.appttude.h_mal.atlas_weather.widget.WidgetState.Companion.getWidgetState
|
||||
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.WidgetWeatherCollection
|
||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
||||
import com.appttude.h_mal.atlas_weather.utils.isInternetAvailable
|
||||
import com.appttude.h_mal.atlas_weather.utils.tryOrNullSuspended
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.LateInitKodein
|
||||
import org.kodein.di.generic.instance
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a JobIntentService used for home screen widget
|
||||
*/
|
||||
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) {
|
||||
// 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.
|
||||
kodein.baseKodein = (applicationContext as KodeinAware).kodein
|
||||
executeWidgetUpdate()
|
||||
}
|
||||
|
||||
private fun executeWidgetUpdate() {
|
||||
val componentName = ComponentName(this, NewAppWidget::class.java)
|
||||
initBaseWidget(componentName)
|
||||
|
||||
initiateWidgetUpdate(getCurrentWidgetState())
|
||||
}
|
||||
|
||||
private fun initiateWidgetUpdate(state: WidgetState) {
|
||||
when (state) {
|
||||
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(fromStorage: Boolean) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val result = getWidgetWeather(fromStorage)
|
||||
appWidgetIds.forEach { id -> setupView(id, result) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateErrorWidget(state: WidgetState) {
|
||||
appWidgetIds.forEach { id -> setEmptyView(id, state) }
|
||||
}
|
||||
|
||||
private fun getCurrentWidgetState(): WidgetState {
|
||||
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)
|
||||
|
||||
return getWidgetState(locationGranted, isScreenOn, internetAvailable)
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
suspend fun getWidgetWeather(storageOnly: Boolean): WidgetWeatherCollection? {
|
||||
return tryOrNullSuspended {
|
||||
if (!storageOnly) helper.fetchData()
|
||||
helper.getWidgetWeatherCollection()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setEmptyView(appWidgetId: Int, state: WidgetState) {
|
||||
val error = when (state) {
|
||||
NO_LOCATION -> "No Location Permission"
|
||||
SCREEN_ON_CONNECTION_UNAVAILABLE -> "No network available"
|
||||
else -> "No data"
|
||||
}
|
||||
|
||||
val views = createRemoteView(R.layout.weather_app_widget)
|
||||
bindErrorView(appWidgetId, views, error)
|
||||
}
|
||||
|
||||
private fun setupView(
|
||||
appWidgetId: Int,
|
||||
collection: WidgetWeatherCollection?
|
||||
) {
|
||||
val views = createRemoteView(R.layout.weather_app_widget)
|
||||
setLastUpdated(views, collection?.widgetData?.timeStamp)
|
||||
views.setInt(R.id.whole_widget_view, "setBackgroundColor", helper.getWidgetBackground())
|
||||
|
||||
if (collection != null) {
|
||||
bindView(appWidgetId, views, collection)
|
||||
} else {
|
||||
bindEmptyView(appWidgetId, views, "No weather available")
|
||||
}
|
||||
}
|
||||
|
||||
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 ->
|
||||
val containerId: Int = resources.getIdentifier("widget_item_$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 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)
|
||||
setImageView(it.icon, remoteViews, imageId, appWidgetId)
|
||||
remoteViews.setOnClickPendingIntent(containerId, clickIntent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLastUpdated(views: RemoteViews, timeStamp: Long?) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && timeStamp != null) {
|
||||
val difference = System.currentTimeMillis().minus(timeStamp)
|
||||
|
||||
val status = if (difference > HALF_DAY) {
|
||||
"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")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Unique job ID for this service.
|
||||
*/
|
||||
private const val JOB_ID = 1000
|
||||
|
||||
/**
|
||||
* Convenience method for enqueuing work in to this service.
|
||||
*/
|
||||
fun enqueueWork(context: Context, work: Intent) {
|
||||
enqueueWork(context, WidgetJobServiceIntent::class.java, JOB_ID, work)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.appttude.h_mal.atlas_weather.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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="com.appttude.h_mal.atlas_weather.monoWeather.ui.world.AddLocationFragment">
|
||||
tools:context="com.appttude.h_mal.atlas_weather.ui.world.AddLocationFragment">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:menu="@menu/tabs_menu" />
|
||||
|
||||
<fragment
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/container"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.appttude.h_mal.atlas_weather.monoWeather.ui.world.WorldFragment">
|
||||
tools:context="com.appttude.h_mal.atlas_weather.ui.world.WorldFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.appttude.h_mal.atlas_weather.monoWeather.ui.world.AddLocationFragment">
|
||||
tools:context="com.appttude.h_mal.atlas_weather.ui.world.AddLocationFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="gone">
|
||||
<ProgressBar
|
||||
android:layout_gravity="center"
|
||||
style="?android:attr/progressBarStyle"
|
||||
@@ -34,6 +34,4 @@
|
||||
android:layout_height="wrap_content"/>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".legacy.ui.home.MainActivity">
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:id="@+id/settings_fragment"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/action_settings"
|
||||
android:icon="@drawable/ic_round_settings_24"
|
||||
|
||||
@@ -27,4 +27,12 @@
|
||||
<item name="android:textColor">#ffffff</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
|
||||
<style name="icon_style__further_deatils">
|
||||
<item name="android:layout_width">48dp</item>
|
||||
<item name="android:layout_height">48dp</item>
|
||||
<item name="android:adjustViewBounds">true</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:tint">@color/colorAccent</item>
|
||||
</style>
|
||||
</resources>
|
||||
6
app/src/main/res/xml/network_security_config.xml
Normal file
6
app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">openweathermap.org</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
@@ -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.monoWeather.ui.widget.WidgetLocationPermissionActivity"
|
||||
android:initialKeyguardLayout="@layout/weather_app_widget"
|
||||
android:initialLayout="@layout/weather_app_widget"
|
||||
android:minHeight="110.0dp"
|
||||
android:minWidth="320.0dp"
|
||||
android:minResizeWidth="320.0dp"
|
||||
android:minResizeHeight="110.0dp"
|
||||
android:previewImage="@drawable/widget_screenshot"
|
||||
android:updatePeriodMillis="1800000"
|
||||
android:resizeMode="vertical"
|
||||
android:widgetCategory="home_screen">
|
||||
|
||||
</appwidget-provider>
|
||||
Reference in New Issue
Block a user