- new flavours added

This commit is contained in:
2020-12-01 19:22:52 +00:00
parent f6e9bf8bcb
commit 9d053bfb60
115 changed files with 3282 additions and 747 deletions

View File

@@ -12,8 +12,8 @@ android {
applicationId "com.appttude.h_mal.atlas_weather"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
versionCode 2
versionName "2.0"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
vectorDrawables.useSupportLibrary = true
@@ -36,12 +36,34 @@ android {
jvmTarget = "1.8"
}
flavorDimensions "default"
productFlavors{
atlasWeather{
applicationIdSuffix ".atlasWeather"
}
monoWeather{
applicationIdSuffix ".monoWeather"
}
}
sourceSets {
atlasWeather {
manifest {
srcFile 'src/atlasWeather/AndroidManifest.xml'
}
}
monoWeather {
manifest {
srcFile 'src/monoWeather/AndroidManifest.xml'
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
@@ -55,7 +77,7 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:rules:1.2.0'
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-test:1.3.71"
implementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
// android unit testing and espresso
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
@@ -76,11 +98,13 @@ dependencies {
androidTestImplementation "io.mockk:mockk-android:$mockk_ver"
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
def retrofit_ver = "2.8.1"
implementation "com.squareup.retrofit2:retrofit:$retrofit_ver"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_ver"
// Shared prefs
implementation "androidx.preference:preference-ktx:1.1.1"
def prefs_ver = "1.1.1"
implementation "androidx.preference:preference-ktx:$prefs_ver"
//Kodein Dependency Injection
def kodein_version = "6.2.1"

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:node="merge">
<activity android:name=".ui.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.settings.UnitSettingsActivity"
android:label="Settings" />
<receiver
android:name=".notification.NotificationReceiver"
android:parentActivityName=".MainActivity" />
<receiver android:name=".widget.NewAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="com.example.h_mal.weather_app.app.ACTION_DATA_UPDATED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/new_app_widget_info" />
</receiver>
<service
android:name=".widget.WidgetRemoteViewsService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
</application>
</manifest>

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.notification
package com.appttude.h_mal.atlas_weather.atlasWeather.notification
import android.graphics.Bitmap

View File

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

View File

@@ -0,0 +1,8 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui
import androidx.appcompat.app.AppCompatActivity
abstract class BaseActivity : AppCompatActivity(){
}

View File

@@ -0,0 +1,55 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.view.View
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
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
abstract class BaseFragment: Fragment(){
// toggle visibility of progress spinner while async operations are taking place
fun progressBarStateObserver(progressBar: View) = Observer<Event<Boolean>> {
when(it.getContentIfNotHandled()){
true -> {
progressBar.show()
}
false -> {
progressBar.hide()
}
}
}
// display a toast when operation fails
fun errorObserver() = Observer<Event<String>> {
it.getContentIfNotHandled()?.let { message ->
displayToast(message)
}
}
@SuppressLint("MissingPermission")
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()
}
}
}
}

View File

@@ -0,0 +1,63 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
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.appttude.h_mal.atlas_weather.atlasWeather.ui.settings.UnitSettingsActivity
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.android.synthetic.atlasWeather.activity_main.*
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
setSupportActionBar(toolbar)
val 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 tabs = setOf(R.id.nav_home, R.id.nav_world)
val appBarConfiguration = AppBarConfiguration(tabs)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
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) {
R.id.action_settings -> {
val i = Intent(this, UnitSettingsActivity::class.java)
startActivity(i)
return true
}
}
return super.onOptionsItemSelected(item)
}
}

View File

@@ -0,0 +1,50 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.WorldItemFragmentArgs
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.adapter.WeatherRecyclerAdapter
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_main.*
class WorldItemFragment : Fragment() {
private var param1: WeatherDisplay? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).weatherDisplay
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerAdapter = WeatherRecyclerAdapter {
val directions =
WorldItemFragmentDirections.actionWorldItemFragmentToFurtherDetailsFragment(it)
navigateTo(directions)
}
param1?.let { recyclerAdapter.addCurrent(it) }
forecast_listview.apply {
layoutManager = LinearLayoutManager(context)
adapter = recyclerAdapter
}
}
}

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui.details
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.details
import android.os.Bundle
import android.view.LayoutInflater

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui.home
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.home
import android.Manifest
import android.annotation.SuppressLint
@@ -13,16 +13,16 @@ import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.ui.WeatherRecyclerAdapter
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.adapter.WeatherRecyclerAdapter
import com.appttude.h_mal.atlas_weather.utils.displayToast
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.activity_add_forecast.*
import kotlinx.android.synthetic.main.fragment_main.*
import kotlinx.android.synthetic.main.fragment_home.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
@@ -63,7 +63,9 @@ class HomeFragment : BaseFragment(), KodeinAware {
swipe_refresh.apply {
setOnRefreshListener {
viewModel.fetchData()
getPermissionResult(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION_PERMISSION_REQUEST){
viewModel.fetchData()
}
isRefreshing = true
}
}

View File

@@ -0,0 +1,6 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.adapter
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)

View File

@@ -0,0 +1,26 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.adapter
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.utils.loadImage
class ViewHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
var locationTV: TextView = listItemView.findViewById(R.id.location_main_4)
var conditionTV: TextView = listItemView.findViewById(R.id.condition_main_4)
var weatherIV: ImageView = listItemView.findViewById(R.id.icon_main_4)
var avgTempTV: TextView = listItemView.findViewById(R.id.temp_main_4)
var tempUnit: TextView = listItemView.findViewById(R.id.temp_unit_4)
fun bindData(weather: WeatherDisplay?){
locationTV.text = weather?.location
conditionTV.text = weather?.description
weatherIV.loadImage(weather?.iconURL)
avgTempTV.text = weather?.averageTemp?.toInt().toString()
tempUnit.text = weather?.unit
}
}

View File

@@ -0,0 +1,28 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.adapter
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
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.utils.loadImage
class ViewHolderForecast(itemView: View) : RecyclerView.ViewHolder(itemView) {
var dateTV: TextView = itemView.findViewById(R.id.list_date)
var dayTV: TextView = itemView.findViewById(R.id.list_day)
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
var mainTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
var minorTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
fun bindView(forecast: Forecast?) {
dateTV.text = forecast?.date
dayTV.text = forecast?.day
conditionTV.text = forecast?.condition
weatherIV.loadImage(forecast?.weatherIcon)
mainTempTV.text = forecast?.mainTemp
minorTempTV.text = forecast?.minorTemp
}
}

View File

@@ -0,0 +1,24 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.adapter
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
internal class ViewHolderFurtherDetails(itemView: View) : RecyclerView.ViewHolder(itemView) {
var windSpeed: TextView = itemView.findViewById(R.id.windspeed)
var windDirection: TextView = itemView.findViewById(R.id.winddirection)
var precipitation: TextView = itemView.findViewById(R.id.precip_)
var humidity: TextView = itemView.findViewById(R.id.humidity_)
var clouds: TextView = itemView.findViewById(R.id.clouds_)
fun bindData(weather: WeatherDisplay?){
windSpeed.text = weather?.windSpeed
windDirection.text = weather?.windDirection
precipitation.text = weather?.precipitation
humidity.text = weather?.humidity
clouds.text = weather?.clouds
}
}

View File

@@ -1,15 +1,12 @@
package com.appttude.h_mal.atlas_weather.ui
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.adapter
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
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.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.utils.generateView
import com.appttude.h_mal.atlas_weather.utils.loadImage
class WeatherRecyclerAdapter(
val itemClick: (Forecast) -> Unit
@@ -28,15 +25,15 @@ class WeatherRecyclerAdapter(
EmptyViewHolder(emptyViewHolder)
}
is ViewType.Current -> {
val viewCurrent = parent.generateView(R.layout.list_item4)
val viewCurrent = parent.generateView(R.layout.list_item_current)
ViewHolderCurrent(viewCurrent)
}
is ViewType.Forecast -> {
val viewForecast = parent.generateView(R.layout.list_item_layout2)
val viewForecast = parent.generateView(R.layout.list_item_forecast)
ViewHolderForecast(viewForecast)
}
is ViewType.Further -> {
val viewFurther = parent.generateView(R.layout.list_item3)
val viewFurther = parent.generateView(R.layout.list_item_further)
ViewHolderFurtherDetails(viewFurther)
}
}
@@ -49,7 +46,7 @@ class WeatherRecyclerAdapter(
object Further : ViewType()
}
private fun getDataType(type: Int): ViewType{
private fun getDataType(type: Int): ViewType {
return when (type){
0 -> ViewType.Empty
1 -> ViewType.Current
@@ -83,7 +80,7 @@ class WeatherRecyclerAdapter(
is ViewType.Forecast -> {
val viewHolderForecast = holder as ViewHolderForecast
weather?.forecast?.get(position)?.let { i ->
weather?.forecast?.get(position - 1)?.let { i ->
viewHolderForecast.bindView(i)
viewHolderForecast.itemView.setOnClickListener {
itemClick(i)
@@ -103,59 +100,4 @@ class WeatherRecyclerAdapter(
return 2 + (weather?.forecast?.size?: 0)
}
internal class ViewHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
var locationTV: TextView = listItemView.findViewById(R.id.location_main_4)
var conditionTV: TextView = listItemView.findViewById(R.id.condition_main_4)
var weatherIV: ImageView = listItemView.findViewById(R.id.icon_main_4)
var avgTempTV: TextView = listItemView.findViewById(R.id.temp_main_4)
var tempUnit: TextView = listItemView.findViewById(R.id.temp_unit_4)
fun bindData(weather: WeatherDisplay?){
locationTV.text = weather?.location
conditionTV.text = weather?.description
weatherIV.loadImage(weather?.iconURL)
avgTempTV.text = weather?.averageTemp?.toInt().toString()
tempUnit.text = weather?.unit
}
}
internal class ViewHolderForecast(itemView: View) : RecyclerView.ViewHolder(itemView) {
var dateTV: TextView = itemView.findViewById(R.id.list_date)
var dayTV: TextView = itemView.findViewById(R.id.list_day)
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
var mainTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
var minorTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
fun bindView(forecast: Forecast?) {
dateTV.text = forecast?.date
dayTV.text = forecast?.day
conditionTV.text = forecast?.condition
weatherIV.loadImage(forecast?.weatherIcon)
mainTempTV.text = forecast?.mainTemp
minorTempTV.text = forecast?.minorTemp
}
}
internal class ViewHolderFurtherDetails(itemView: View) : RecyclerView.ViewHolder(itemView) {
var windSpeed: TextView = itemView.findViewById(R.id.windspeed)
var windDirection: TextView = itemView.findViewById(R.id.winddirection)
var precipitation: TextView = itemView.findViewById(R.id.precip_)
var humidity: TextView = itemView.findViewById(R.id.humidity_)
var clouds: TextView = itemView.findViewById(R.id.clouds_)
fun bindData(weather: WeatherDisplay?){
windSpeed.text = weather?.windSpeed
windDirection.text = weather?.windDirection
precipitation.text = weather?.precipitation
humidity.text = weather?.humidity
clouds.text = weather?.clouds
}
}
internal class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
}

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui.settings
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.settings
import android.app.AlarmManager
import android.app.PendingIntent
@@ -12,13 +12,12 @@ import android.preference.PreferenceActivity
import android.preference.PreferenceFragment
import androidx.preference.PreferenceManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.notification.NotificationReceiver
import com.appttude.h_mal.atlas_weather.widget.NewAppWidget
import com.appttude.h_mal.atlas_weather.atlasWeather.notification.NotificationReceiver
import com.appttude.h_mal.atlas_weather.atlasWeather.widget.NewAppWidget
import java.util.*
class UnitSettingsActivity : PreferenceActivity() {
private val TAG = javaClass.simpleName
private var prefListener: OnSharedPreferenceChangeListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui.world
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.world
import android.os.Bundle
import android.view.LayoutInflater
@@ -7,7 +7,7 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.observe
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.utils.displayToast
import com.appttude.h_mal.atlas_weather.utils.goBack
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui.world
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.world
import android.os.Bundle
import android.view.LayoutInflater
@@ -9,7 +9,9 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.world.WorldRecyclerAdapter
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.world.WorldFragmentDirections
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui.world
package com.appttude.h_mal.atlas_weather.atlasWeather.ui.world
import android.view.View
import android.view.ViewGroup

View File

@@ -0,0 +1,37 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.widget
import android.app.Activity
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
import androidx.annotation.LayoutRes
abstract class BaseWidgetClass : AppWidgetProvider(){
fun createRemoteView(context: Context, @LayoutRes id: Int): RemoteViews {
return RemoteViews(context.packageName, id)
}
fun AppWidgetProvider.createUpdatePendingIntent(context: Context, appWidgetId: Int): PendingIntent? {
val seconds = (System.currentTimeMillis() / 1000L).toInt()
val intentUpdate = Intent(context, this::class.java)
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
val idArray = intArrayOf(appWidgetId)
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
return PendingIntent.getBroadcast(
context, seconds, intentUpdate,
PendingIntent.FLAG_UPDATE_CURRENT)
}
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)
}
}

View File

@@ -0,0 +1,77 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.widget
import android.appwidget.AppWidgetManager
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.LateInitKodein
import org.kodein.di.generic.instance
class MyWidgetRemoteViewsFactory(
private val context: Context,
val intent: Intent
) : RemoteViewsFactory{
private val TAG = "MyWidgetRemoteViewsFactory"
private val kodein = LateInitKodein()
private val helper : ServicesHelper by kodein.instance()
private var appWidgetId: Int? = 0
private var list: List<InnerWidgetData>? = null
init {
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID)
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
}
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 = 1
override fun getItemId(i: Int): Long = i.toLong()
override fun hasStableIds(): Boolean {
return true
}
}

View File

@@ -0,0 +1,141 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.widget
import android.Manifest
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.net.Uri
import android.util.Log
import android.widget.RemoteViews
import androidx.core.app.ActivityCompat
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.atlasWeather.ui.MainActivity
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
import com.appttude.h_mal.atlas_weather.model.widget.WidgetData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.kodein.di.KodeinAware
import org.kodein.di.LateInitKodein
import org.kodein.di.generic.instance
/**
* Implementation of App Widget functionality.
*/
private val TAG = NewAppWidget::class.java.simpleName
class NewAppWidget : BaseWidgetClass() {
private val kodein = LateInitKodein()
private val helper : ServicesHelper by kodein.instance()
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
// There may be multiple widgets active, so update all of them
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return
}
CoroutineScope(Dispatchers.IO).launch {
val results = helper.fetchData()
if (results) return@launch
val weatherWidgetCurrent = helper.getWidgetWeather()
withContext(Dispatchers.Main){
for (appWidgetId in appWidgetIds) {
val updatePendingIntent = createUpdatePendingIntent(context, appWidgetId)
val views = createRemoteView(context, R.layout.new_app_widget)
bindView(context, appWidgetId, views, updatePendingIntent, weatherWidgetCurrent)
}
super.onUpdate(context, appWidgetManager, appWidgetIds)
}
}
}
override fun onEnabled(context: Context) {
try {
val appWidgetManager = AppWidgetManager.getInstance(context)
val thisAppWidget = ComponentName(context.packageName, NewAppWidget::class.java.name)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
onUpdate(context, appWidgetManager, appWidgetIds)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_listview)
} catch (e: Exception) {
Log.e(TAG, "onEnabled: ", e)
}
}
override fun onDisabled(context: Context) {
// Enter relevant functionality for when the last widget is disabled
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action ==
AppWidgetManager.ACTION_APPWIDGET_UPDATE) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val thisAppWidget = ComponentName(context.packageName, NewAppWidget::class.java.name)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_listview)
}
super.onReceive(context, intent)
}
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))
}
}
private fun bindView(
context: Context,
appWidgetId: Int,
views: RemoteViews,
updatePendingIntent: PendingIntent?,
weather: WidgetData?){
val appWidgetManager = AppWidgetManager.getInstance(context)
views.setInt(R.id.whole_widget_view, "setBackgroundColor", helper.getWidgetBackground())
weather?.let {
val intent = createForecastListIntent(
context,
appWidgetId
)
views.setRemoteAdapter(R.id.widget_listview, intent)
views.setTextViewText(R.id.widget_main_temp, it.currentTemp)
views.setTextViewText(R.id.widget_feel_temp, "°C")
views.setTextViewText(R.id.widget_current_location, it.location)
views.setImageViewResource(R.id.location_icon, R.drawable.location_flag)
views.setImageViewBitmap(R.id.widget_current_icon, it.icon)
val clickPendingIntentTemplate = createClickingPendingIntent(context, MainActivity::class.java)
views.setPendingIntentTemplate(R.id.widget_listview, clickPendingIntentTemplate)
views.setOnClickPendingIntent(R.id.widget_current_icon, updatePendingIntent)
views.setOnClickPendingIntent(R.id.widget_current_location, updatePendingIntent)
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
return
}
Log.i(TAG, "onPostExecute: weather is empty")
views.setTextViewText(R.id.widget_current_location, "Refresh")
views.setImageViewResource(R.id.widget_current_icon, R.drawable.widget_error_icon)
views.setImageViewResource(R.id.location_icon, R.drawable.refreshing)
views.setOnClickPendingIntent(R.id.widget_current_icon, updatePendingIntent)
views.setOnClickPendingIntent(R.id.widget_current_location, updatePendingIntent)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}

View File

@@ -0,0 +1,10 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.widget
import android.content.Intent
import android.widget.RemoteViewsService
class WidgetRemoteViewsService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
return MyWidgetRemoteViewsFactory(applicationContext, intent)
}
}

View File

@@ -0,0 +1,15 @@
package com.appttude.h_mal.atlas_weather.atlasWeather.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)
}

View File

@@ -0,0 +1,311 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="12dp"
android:layout_weight="2">
<ImageView
android:src="@drawable/somethingnew"
style="@style/icon_style__further_deatils" />
</FrameLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="@string/max" />
<TextView
android:id="@+id/maxtemp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
tools:text="11" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="@string/average" />
<TextView
android:id="@+id/averagetemp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
tools:text="11" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="@string/min" />
<TextView
android:id="@+id/minimumtemp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
tools:text="11" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="12dp"
android:layout_weight="2">
<ImageView
android:src="@drawable/breeze"
style="@style/icon_style__further_deatils" />
</FrameLayout>
<RelativeLayout
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_weight="3">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Wind: " />
<TextView
android:id="@+id/windtext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="7mph"
/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="12dp"
android:layout_weight="2">
<ImageView
android:src="@drawable/water_drop"
style="@style/icon_style__further_deatils" />
</FrameLayout>
<RelativeLayout
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Humidity: " />
<TextView
android:id="@+id/humiditytext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="85%"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Precip: " />
<TextView
android:id="@+id/preciptext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="11mm"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="12dp"
android:layout_weight="2">
<ImageView
android:src="@drawable/sunrise"
style="@style/icon_style__further_deatils" />
</FrameLayout>
<RelativeLayout
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="UV: " />
<TextView
android:id="@+id/uvtext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="7"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Sunrise:" />
<TextView
android:id="@+id/sunrisetext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="05:30am"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Sunset:" />
<TextView
android:id="@+id/sunsettext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="06:12pm"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:popupTheme="@style/AppTheme.PopupOverlay"
tools:title="Atlas Weather"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="60dp"
app:itemIconTint="@android:color/background_light"
app:itemTextColor="@android:color/background_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:backgroundTint="@android:color/transparent"
app:elevation="0dp"
app:menu="@menu/tabs_menu" />
<fragment
android:id="@+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar"
tools:layout="@layout/fragment_home"/>
<ProgressBar
android:id="@+id/progress_circular"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="0.2dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -20,6 +20,7 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginRight="6dp"
android:tint="@color/colorAccent"
android:src="@drawable/location_flag" />
<TextView
@@ -27,7 +28,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/colour_five"
android:textSize="22sp"
tools:text="Brisbane" />
@@ -40,7 +41,7 @@
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:includeFontPadding="false"
android:textColor="@color/colour_five"
android:textSize="90sp"
tools:text="24" />
@@ -53,7 +54,7 @@
android:layout_toRightOf="@id/temp_main_4"
android:includeFontPadding="false"
android:text="°C"
android:textColor="@color/colour_five"
android:textSize="30sp" />
<LinearLayout
@@ -73,8 +74,8 @@
<ImageView
android:id="@+id/icon_main_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="64dp"
android:layout_height="64dp"
tools:src="@drawable/day_305" />
<LinearLayout
@@ -91,7 +92,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:textColor="@color/colour_five"
tools:text="Patchy rain possible" />
</LinearLayout>

View File

@@ -7,13 +7,12 @@
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="22dp"
app:cardElevation="0dp"
android:backgroundTint="@android:color/transparent"
card_view:cardPreventCornerOverlap="false"
card_view:cardUseCompatPadding="true">
@@ -34,7 +33,6 @@
android:id="@+id/list_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colour_five"
android:textSize="22sp"
tools:text="Nov 28" />
@@ -42,7 +40,6 @@
android:id="@+id/list_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colour_five"
tools:text="Wednesday" />
</LinearLayout>
@@ -73,7 +70,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textColor="@color/colour_five"
android:textSize="28sp"
tools:text="28" />

View File

@@ -12,7 +12,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="12dp"
android:layout_weight="1">
android:layout_weight="2">
<ImageView
android:src="@drawable/breeze"
@@ -51,7 +51,7 @@
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="11kmp"
android:textColor="#000000" />
/>
</LinearLayout>
<LinearLayout
@@ -71,7 +71,7 @@
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="SW"
android:textColor="#000000" />
/>
</LinearLayout>
@@ -89,7 +89,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="12dp"
android:layout_weight="1">
android:layout_weight="2">
<ImageView
style="@style/icon_style__further_deatils"
@@ -127,7 +127,7 @@
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="85%"
android:textColor="#000000" />
/>
</LinearLayout>
<LinearLayout
@@ -138,7 +138,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Precipitation: " />
android:text="Precip: " />
<TextView
android:id="@+id/precip_"
@@ -146,7 +146,7 @@
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="11mm"
android:textColor="#000000" />
/>
</LinearLayout>
@@ -164,7 +164,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="12dp"
android:layout_weight="1">
android:layout_weight="2">
<ImageView
style="@style/icon_style__further_deatils"
@@ -203,7 +203,7 @@
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="85%"
android:textColor="#000000" />
/>
</LinearLayout>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_navigation"
app:startDestination="@id/nav_home"
tools:ignore="UnusedNavigation">
<fragment
android:id="@+id/nav_home"
android:name="com.appttude.h_mal.atlas_weather.atlasWeather.ui.home.HomeFragment"
android:label="Home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_furtherDetailsFragment"
app:destination="@id/furtherDetailsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_open_enter"
app:popExitAnim="@anim/fragment_open_exit" />
</fragment>
<fragment
android:id="@+id/furtherDetailsFragment"
android:name="com.appttude.h_mal.atlas_weather.atlasWeather.ui.details.FurtherInfoFragment"
android:label="Further Details">
<argument
android:name="forecast"
app:argType="com.appttude.h_mal.atlas_weather.model.forecast.Forecast" />
</fragment>
<fragment
android:id="@+id/nav_world"
android:name="com.appttude.h_mal.atlas_weather.atlasWeather.ui.world.WorldFragment"
android:label="World"
tools:layout="@layout/fragment__two">
<action
android:id="@+id/action_worldFragment_to_addLocationFragment"
app:destination="@id/addLocationFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_open_enter"
app:popExitAnim="@anim/fragment_open_exit" />
<action
android:id="@+id/action_worldFragment_to_worldItemFragment"
app:destination="@id/worldItemFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_open_enter"
app:popExitAnim="@anim/fragment_open_exit" />
</fragment>
<fragment
android:id="@+id/addLocationFragment"
android:name="com.appttude.h_mal.atlas_weather.atlasWeather.ui.world.AddLocationFragment"
android:label="Add Weather Location"
tools:layout="@layout/activity_add_forecast" />
<fragment
android:id="@+id/worldItemFragment"
android:name="com.appttude.h_mal.atlas_weather.atlasWeather.ui.WorldItemFragment"
android:label="Overview"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_worldItemFragment_to_furtherDetailsFragment"
app:destination="@id/furtherDetailsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_open_enter"
app:popExitAnim="@anim/fragment_open_exit" />
<argument
android:name="weatherDisplay"
app:argType="com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay" />
</fragment>
</navigation>

View File

@@ -0,0 +1,4 @@
<resources>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>

View File

@@ -8,6 +8,7 @@
<item name="colorAccent">@color/colour_one</item>
<item name="android:windowBackground">@drawable/gradient</item>
<item name="fontFamily">sans-serif-light</item>
<item name="android:textColor">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
@@ -50,7 +51,7 @@
<item name="android:layout_height">64dp</item>
<item name="android:adjustViewBounds">true</item>
<item name="android:layout_gravity">center</item>
<item name="android:tint">@color/colour_five</item>
<item name="android:tint">@color/colorAccent</item>
</style>
</resources>

View File

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

View File

@@ -1,8 +1,8 @@
<?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_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
@@ -15,50 +15,4 @@
android:name="android.hardware.location.gps"
android:required="true" />
<application
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.appttude.h_mal.atlas_weather.ui.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.appttude.h_mal.atlas_weather.ui.settings.UnitSettingsActivity"
android:label="Settings" />
<receiver
android:name=".notification.NotificationReceiver"
android:parentActivityName="com.appttude.h_mal.atlas_weather.MainActivity" />
<receiver android:name=".widget.NewAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="com.example.h_mal.weather_app.app.ACTION_DATA_UPDATED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/new_app_widget_info" />
</receiver>
<service
android:name=".widget.WidgetRemoteViewsService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
</application>
</manifest>

View File

@@ -11,7 +11,7 @@ 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.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.notification.ServicesHelper
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
import com.google.gson.Gson
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware

View File

@@ -37,7 +37,6 @@ class LocationProvider(
return result?.let { location ->
location.locality?.let { return it }
location.thoroughfare?.let { return it }
location.subAdminArea?.let { return it }
} ?: "$lat, $long"
}

View File

@@ -14,6 +14,6 @@ interface Repository {
suspend fun loadSingleCurrentWeatherFromRoom(id: String): EntityItem
fun isSearchValid(locationName: String): Boolean
fun saveLastSavedAt(locationName: String)
suspend fun deleteSavedWeatherEntry(locationName: String)
suspend fun deleteSavedWeatherEntry(locationName: String): Boolean
fun getSavedLocations(): List<String>
}

View File

@@ -56,9 +56,9 @@ class RepositoryImpl(
prefs.saveLastSavedAt("$LOCATION_CONST$locationName")
}
override suspend fun deleteSavedWeatherEntry(locationName: String){
db.getSimpleDao().deleteEntry(locationName)
override suspend fun deleteSavedWeatherEntry(locationName: String): Boolean {
prefs.deleteLocation(locationName)
return db.getSimpleDao().deleteEntry(locationName) > 0
}
override fun getSavedLocations(): List<String> {

View File

@@ -1,5 +1,6 @@
package com.appttude.h_mal.atlas_weather.data.room
import android.text.BoringLayout
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
@@ -27,6 +28,6 @@ interface WeatherDao {
fun getAllFullWeatherWithoutCurrent(id: String = CURRENT_LOCATION) : LiveData<List<EntityItem>>
@Query("DELETE FROM EntityItem WHERE id = :userId")
suspend fun deleteEntry(userId: String)
suspend fun deleteEntry(userId: String): Int
}

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.notification
package com.appttude.h_mal.atlas_weather.helper
import android.Manifest
import android.graphics.Bitmap
@@ -90,7 +90,7 @@ class ServicesHelper(
}
fun getBitmapFromUrl(imageAddress: String?): Bitmap? {
private fun getBitmapFromUrl(imageAddress: String?): Bitmap? {
return try {
val url = URL(imageAddress)
BitmapFactory.decodeStream(url.openConnection().getInputStream())

View File

@@ -41,10 +41,10 @@ data class WeatherDisplay(
null,
weather.current?.icon,
weather.current?.description,
weather.daily?.map { Forecast(it) },
weather.daily?.drop(1)?.map { Forecast(it) },
weather.current?.windSpeed?.toString(),
weather.current?.windDeg?.toString(),
weather.daily?.get(0)?.pop?.toString(),
weather.daily?.get(0)?.pop?.times(100)?.toString(),
weather.current?.humidity?.toString(),
weather.current?.clouds?.toString(),
weather.lat,

View File

@@ -42,7 +42,7 @@ data class WidgetData(
data class InnerWidgetData(
val date: String?,
val icon: Bitmap?,
val currentTemp: String?
val highTemp: String?
):Parcelable{
constructor(parcel: Parcel) : this(
@@ -54,7 +54,7 @@ data class InnerWidgetData(
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(date)
parcel.writeParcelable(icon, flags)
parcel.writeString(currentTemp)
parcel.writeString(highTemp)
}
override fun describeContents(): Int {

View File

@@ -1,30 +0,0 @@
package com.appttude.h_mal.atlas_weather.ui.home
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
abstract class BaseActivity : AppCompatActivity(){
@SuppressLint("MissingPermission")
fun getPermissionResult(
permission: String,
permissionCode: Int,
permissionGranted: () -> Unit
){
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(permission), permissionCode)
return
}else{
CoroutineScope(Dispatchers.Main).launch{
permissionGranted.invoke()
}
}
}
}

View File

@@ -1,6 +1,7 @@
package com.appttude.h_mal.atlas_weather.utils
import android.content.Context
import android.content.res.Resources
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -30,7 +31,7 @@ fun Fragment.displayToast(message: String) {
fun ImageView.loadImage(url: String?){
Picasso.get()
.load(url)
.placeholder(R.mipmap.ic_launcher)
.error(R.drawable.ic_baseline_cloud_off_24)
.into(this)
}
@@ -41,12 +42,14 @@ fun ViewGroup.generateView(layoutId: Int): View = LayoutInflater
fun ImageView.loadImage(url: String?, height: Int, width: Int){
Picasso.get()
.load(url)
.resize(width, height)
.resize(width.dpToPx(), height.dpToPx())
.centerCrop()
.placeholder(R.mipmap.ic_launcher)
.error(R.drawable.ic_baseline_cloud_off_24)
.into(this)
}
fun Int.dpToPx(): Int = (this * Resources.getSystem().displayMetrics.density).toInt()
fun SearchView.onSubmitListener(searchSubmit: (String) -> Unit) {
this.setOnQueryTextListener(object :
SearchView.OnQueryTextListener {

View File

@@ -20,7 +20,6 @@ class MainViewModel(
private val repository: Repository
): ViewModel(){
val currentWeatherLiveData = MutableLiveData<Current>()
val weatherLiveData = MutableLiveData<WeatherDisplay>()
val operationState = MutableLiveData<Event<Boolean>>()

View File

@@ -30,6 +30,10 @@ class WorldViewModel(
init {
weatherListLiveData.observeForever {
it.forEach { item->
println(item)
}
val list = it.map { data ->
WeatherDisplay(data.weather).apply {
unit = "°C"
@@ -43,7 +47,7 @@ class WorldViewModel(
fun fetchDataForSingleLocation(locationName: String) {
CoroutineScope(Dispatchers.IO).launch {
operationState.postValue(Event(true))
// Check if location exists
if (repository.getSavedLocations().contains(locationName)){
operationError.postValue(Event("$locationName already exists"))
return@launch
@@ -53,14 +57,24 @@ class WorldViewModel(
// Get location
val latLong =
locationProvider.getLatLongFromLocationString(locationName)
val lat = latLong.first
val lon = latLong.second
// Get weather from api
val weather = repository
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
.getWeatherFromApi(lat.toString(), lon.toString())
// retrieved location name
val retrievedLocation = locationProvider.getLocationName(weather.lat, weather.lon)
if (repository.getSavedLocations().contains(retrievedLocation)){
operationError.postValue(Event("$retrievedLocation already exists"))
return@launch
}
// Save data if not null
weather.let {
repository.saveCurrentWeatherToRoom(locationName, it)
operationComplete.postValue(Event("$locationName saved"))
repository.saveCurrentWeatherToRoom(retrievedLocation, it)
operationComplete.postValue(Event("$retrievedLocation saved"))
return@launch
}
} catch (e: IOException) {
@@ -100,4 +114,20 @@ class WorldViewModel(
}
}
}
fun deleteLocation(locationName: String){
CoroutineScope(Dispatchers.IO).launch {
operationState.postValue(Event(true))
try {
val success = repository.deleteSavedWeatherEntry(locationName)
if (!success){
operationError.postValue(Event("Failed to delete"))
}
} catch (e: IOException) {
operationError.postValue(Event(e.message!!))
} finally {
operationState.postValue(Event(false))
}
}
}
}

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/black" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4c-1.48,0 -2.85,0.43 -4.01,1.17l1.46,1.46C10.21,6.23 11.08,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3 0,1.13 -0.64,2.11 -1.56,2.62l1.45,1.45C23.16,18.16 24,16.68 24,15c0,-2.64 -2.05,-4.78 -4.65,-4.96zM3,5.27l2.75,2.74C2.56,8.15 0,10.77 0,14c0,3.31 2.69,6 6,6h11.73l2,2L21,20.73 4.27,4 3,5.27zM7.73,10l8,8H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h1.73z"/>
</vector>

View File

@@ -6,71 +6,45 @@
android:orientation="vertical"
tools:context=".legacy.ui.AddForecastActivity">
<ProgressBar
android:id="@+id/pb_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/whole_view"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_margin="24dp"
android:orientation="vertical">
<LinearLayout
<EditText
android:id="@+id/location_name_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_marginLeft="24dp"
android:layout_marginRight="12dp"
android:orientation="horizontal"
android:paddingBottom="@dimen/activity_horizontal_margin">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Location:"
android:textAppearance="@style/TextAppearance.AppCompat"
android:textColor="@color/colour_five" />
<EditText
android:id="@+id/location_name_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:ems="10"
android:hint="Location name"
android:inputType="textPersonName" />
</LinearLayout>
android:layout_weight="2"
android:ems="10"
android:hint="@string/location_name"
android:inputType="textPersonName"
android:maxLines="2"
tools:text="Greater London"/>
<Button
android:id="@+id/submit"
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="24dp"
android:layout_marginBottom="36dp"
android:background="@drawable/button_layout"
android:layout_gravity="right"
android:text="@string/submit"
android:textColor="#ffffff"
android:textStyle="bold"
style="?android:attr/borderlessButtonStyle"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
tools:visibility="visible"/>
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
tools:visibility="visible" />
</RelativeLayout>

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".legacy.ui.home.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:titleTextAppearance="@style/titlebar"
tools:fontfamily="@font/archeologicaps"
tools:title="Atlas Weather">
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:tab="hello"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
</androidx.viewpager.widget.ViewPager>
<ProgressBar
android:id="@+id/progress_circular"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -12,7 +12,6 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
@@ -24,8 +23,8 @@
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="60dp"
android:background="?android:attr/windowBackground"
app:itemIconTint="@android:color/background_light"
app:itemTextColor="@android:color/background_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
@@ -40,7 +39,8 @@
app:layout_constraintBottom_toTopOf="@id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar"/>
app:layout_constraintTop_toBottomOf="@id/toolbar"
tools:layout="@layout/fragment_home"/>
<ProgressBar
android:id="@+id/progress_circular"

View File

@@ -1,96 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="1dp">
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.cardview.widget.CardView
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="22dp"
card_view:cardPreventCornerOverlap="false"
card_view:cardUseCompatPadding="true">
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="15dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/db_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colour_five"
android:textSize="22sp"
tools:text="Bakkafjörður"
android:includeFontPadding="false"/>
</LinearLayout>
<ImageView
android:id="@+id/db_icon"
android:id="@+id/location_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginRight="12dp"
android:tint="@color/colorAccent"
app:srcCompat="@drawable/maps_and_flags" />
<TextView
android:id="@+id/db_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:src="@drawable/day_305" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginRight="15dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:id="@+id/list_temp_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:orientation="horizontal">
<TextView
android:id="@+id/db_main_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textColor="@color/colour_five"
android:textSize="28sp"
tools:text="28" />
<TextView
android:id="@+id/db_minor_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:includeFontPadding="false"
tools:text="°C" />
</LinearLayout>
<TextView
android:id="@+id/db_condition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:includeFontPadding="false"
android:gravity="right"
tools:text="Patchy rain possible" />
</LinearLayout>
android:layout_height="match_parent"
android:textSize="22sp"
android:maxLines="2"
tools:text="Greater London" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/db_icon"
android:layout_width="64dp"
android:layout_height="64dp"
tools:padding="10dp"
tools:tint="@color/colorAccent"
tools:src="@drawable/cloud_symbol" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:id="@+id/list_temp_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:orientation="horizontal">
<TextView
android:id="@+id/db_main_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textSize="22sp"
tools:text="28" />
<TextView
android:id="@+id/db_minor_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:includeFontPadding="false"
tools:text="°C" />
</LinearLayout>
<TextView
android:id="@+id/db_condition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:gravity="right"
android:textSize="12sp"
android:includeFontPadding="false"
tools:text="Patchy rain possible" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@@ -3,14 +3,19 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".legacy.ui.home.FragmentTwo">
tools:context=".ui.home.FragmentTwo">
<ListView
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview"
android:divider="@null">
</ListView>
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:id="@+id/world_recycler"
android:divider="@null"
tools:listitem="@layout/db_list_item">
</androidx.recyclerview.widget.RecyclerView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
@@ -18,15 +23,9 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="20dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:clickable="true"
app:srcCompat="@android:drawable/ic_input_add"
android:tint="@android:color/white"
app:elevation="0dp"
android:focusable="true" />
app:srcCompat="@drawable/ic_baseline_add_24"/>
<ProgressBar
android:id="@+id/progressBar2"
@@ -34,5 +33,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
android:visibility="gone"
tools:visibility="visible"/>
</RelativeLayout>

View File

@@ -3,8 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".legacy.ui.home.MainActivity">
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="wrap_content"
@@ -14,7 +13,8 @@
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/forecast_listview">
android:id="@+id/forecast_listview"
tools:listitem="@layout/list_item_further">
</androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@@ -5,68 +5,9 @@
android:layout_height="match_parent"
tools:context=".legacy.ui.home.MainActivity">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/swipe_refresh">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/forecast_listview">
</androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<DatePicker
android:layout_width="match_parent"
android:layout_height="match_parent"
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="@+id/mainPB"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
android:id="@+id/emptyView">
<ImageView
android:layout_width="64dp"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:alpha="0.8"
android:src="@drawable/error_icon"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="List Empty"
android:textSize="30sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/emptyViewText"
android:text="check connection or location settings" />
<Button
android:id="@+id/emptyViewButton"
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="6dp"
android:background="@drawable/button_layout"
android:padding="12dp"
android:text="Grant Permission"
android:textColor="#ffffff"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="64dp"/>
</LinearLayout>
/>
</RelativeLayout>

View File

@@ -86,6 +86,16 @@
android:layout_centerInParent="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/location_icon"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_gravity="center"
android:layout_marginRight="2dp"
android:adjustViewBounds="true"
android:src="@drawable/location_flag"
android:tint="@android:color/white" />
<TextView
android:id="@+id/widget_current_location"
android:layout_width="wrap_content"
@@ -100,16 +110,6 @@
android:textColor="#ffffff"
android:textSize="12sp"
tools:text="South Brisbane" />
<ImageView
android:id="@+id/location_icon"
android:layout_width="match_parent"
android:layout_height="16dp"
android:layout_gravity="center"
android:layout_marginLeft="2dp"
android:adjustViewBounds="true"
android:src="@drawable/location_flag"
android:tint="@android:color/white" />
</LinearLayout>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="colorPrimary">@android:color/black</color>
<color name="colorPrimaryDark">@android:color/black</color>
<color name="colorAccent">@android:color/white</color>
<color name="colour_one">#E8D0DD</color>
<color name="colour_two">#5F8E7B</color>

View File

@@ -1,72 +1,18 @@
<resources>
<string name="app_name">Atlas Weather</string>
<string name="action_settings">Settings</string>
<string name="action_info">Info</string>
<string name="section_format">Hello World from section: %1$d</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="title_activity_settings">Settings</string>
<!-- Strings related to Settings -->
<!-- Example General settings -->
<string name="pref_header_general">General</string>
<string name="pref_title_social_recommendations">Enable social recommendations</string>
<string name="pref_description_social_recommendations">Recommendations for people to contact
based on your message history
</string>
<string name="pref_title_display_name">Display name</string>
<string name="pref_default_display_name">John Smith</string>
<string name="pref_title_add_friends_to_messages">Add friends to messages</string>
<string-array name="pref_example_list_titles">
<item>Always</item>
<item>When possible</item>
<item>Never</item>
</string-array>
<string-array name="pref_example_list_values">
<item>1</item>
<item>0</item>
<item>-1</item>
</string-array>
<!-- Example settings for Data & Sync -->
<string name="pref_header_data_sync">Data &amp; sync</string>
<string name="pref_title_sync_frequency">Sync frequency</string>
<string-array name="pref_sync_frequency_titles">
<item>15 minutes</item>
<item>30 minutes</item>
<item>1 hour</item>
<item>3 hours</item>
<item>6 hours</item>
<item>Never</item>
</string-array>
<string-array name="pref_sync_frequency_values">
<item>15</item>
<item>30</item>
<item>60</item>
<item>180</item>
<item>360</item>
<item>-1</item>
</string-array>
<string-array name="list_preference_temp">
<item>°C</item>
<item>°F</item>
</string-array>
<string-array name="list_preference_refactor_values">
<item>c</item>
<item>f</item>
</string-array>
<string-array name="list_preference_wind">
<item>MPH</item>
<item>KPH</item>
<item>MpH</item>
<item>kpH</item>
</string-array>
<string-array name="list_preference_wind_values">
@@ -74,16 +20,6 @@
<item>kph</item>
</string-array>
<string-array name="list_preference_pressure">
<item>mBar</item>
<item>In</item>
</string-array>
<string-array name="list_preference_pressure_values">
<item>mb</item>
<item>in</item>
</string-array>
<string-array name="list_preference_precip">
<item>mm</item>
<item>In</item>
@@ -94,11 +30,6 @@
<item>in</item>
</string-array>
<string-array name="list_preference_days">
<item>7</item>
<item>5</item>
</string-array>
<string-array name="list_preference_vis">
<item>KM</item>
<item>Miles</item>
@@ -109,36 +40,18 @@
<item>miles</item>
</string-array>
<string-array name="multi_select_list_preference_default_value" />
<string name="pref_title_system_sync_settings">System sync settings</string>
<!-- Example settings for Notifications -->
<string name="pref_header_notifications">Notifications</string>
<string name="pref_title_new_message_notifications">New message notifications</string>
<string name="pref_title_ringtone">Ringtone</string>
<string name="pref_ringtone_silent">Silent</string>
<string name="pref_title_vibrate">Vibrate</string>
<string name="submit">submit</string>
<string name="insert_entry_failed">Insertion failed</string>
<string name="insert_entry_successful">Insertion successful</string>
<string-array name="list_preference_entries">
<item>Entry 1</item>
<item>Entry 2</item>
<item>Entry 3</item>
</string-array>
<string-array name="list_preference_entry_values">
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string name="appwidget_text">EXAMPLE</string>
<string name="add_widget">Add widget</string>
<string name="title_home">Home</string>
<string name="title_world">World</string>
<string name="location_name">Enter Location name</string>
<string name="degrees_c">°C</string>
<string name="min">Min:</string>
<string name="max">Max:</string>
<string name="average">Average:</string>
</resources>

View File

@@ -1,21 +0,0 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
dismiss it. -->
<!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
<ListPreference
android:defaultValue="180"
android:entries="@array/pref_sync_frequency_titles"
android:entryValues="@array/pref_sync_frequency_values"
android:key="sync_frequency"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="@string/pref_title_sync_frequency" />
<!-- This preference simply launches an intent when selected. Use this UI sparingly, per
design guidelines. -->
<Preference android:title="@string/pref_title_system_sync_settings">
<intent android:action="android.settings.SYNC_SETTINGS" />
</Preference>
</PreferenceScreen>

View File

@@ -1,33 +0,0 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="true"
android:key="example_switch"
android:summary="@string/pref_description_social_recommendations"
android:title="@string/pref_title_social_recommendations" />
<!-- NOTE: EditTextPreference accepts EditText attributes. -->
<!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. -->
<EditTextPreference
android:capitalize="words"
android:defaultValue="@string/pref_default_display_name"
android:inputType="textCapWords"
android:key="example_text"
android:maxLines="1"
android:selectAllOnFocus="true"
android:singleLine="true"
android:title="@string/pref_title_display_name" />
<!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
dismiss it. -->
<!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
<ListPreference
android:defaultValue="-1"
android:entries="@array/pref_example_list_titles"
android:entryValues="@array/pref_example_list_values"
android:key="example_list"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="@string/pref_title_add_friends_to_messages" />
</PreferenceScreen>

View File

@@ -1,20 +0,0 @@
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<!-- These settings headers are only used on tablets. -->
<header
android:fragment="com.appttude.h_mal.atlas_weather.SettingsActivity$GeneralPreferenceFragment"
android:icon="@drawable/ic_info_black_24dp"
android:title="@string/pref_header_general" />
<header
android:fragment="com.appttude.h_mal.atlas_weather.SettingsActivity$NotificationPreferenceFragment"
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/pref_header_notifications" />
<header
android:fragment="com.appttude.h_mal.atlas_weather.SettingsActivity$DataSyncPreferenceFragment"
android:icon="@drawable/ic_sync_black_24dp"
android:title="@string/pref_header_data_sync" />
</preference-headers>

View File

@@ -1,27 +0,0 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A 'parent' preference, which enables/disables child prefs (below)
when checked/unchecked. -->
<SwitchPreference
android:defaultValue="true"
android:key="notifications_new_message"
android:title="@string/pref_title_new_message_notifications" />
<!-- Allows the user to choose a ringtone in the 'notification' category. -->
<!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
<!-- NOTE: RingtonePreference's summary should be set to its value by the activity code. -->
<RingtonePreference
android:defaultValue="content://settings/system/notification_sound"
android:dependency="notifications_new_message"
android:key="notifications_new_message_ringtone"
android:ringtoneType="notification"
android:title="@string/pref_title_ringtone" />
<!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
<SwitchPreference
android:defaultValue="true"
android:dependency="notifications_new_message"
android:key="notifications_new_message_vibrate"
android:title="@string/pref_title_vibrate" />
</PreferenceScreen>

View File

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

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:node="merge">
<activity android:name=".ui.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.settings.UnitSettingsActivity"
android:label="Settings" />
<receiver
android:name=".notification.NotificationReceiver"
android:parentActivityName=".MainActivity" />
<receiver android:name=".widget.NewAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="com.example.h_mal.weather_app.app.ACTION_DATA_UPDATED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/new_app_widget_info" />
</receiver>
<service
android:name=".widget.WidgetRemoteViewsService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,8 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui
import androidx.appcompat.app.AppCompatActivity
abstract class BaseActivity : AppCompatActivity(){
}

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui
package com.appttude.h_mal.atlas_weather.monoWeather.ui
import android.annotation.SuppressLint
import android.content.pm.PackageManager

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui
package com.appttude.h_mal.atlas_weather.monoWeather.ui
import android.content.Intent
import android.os.Bundle
@@ -10,14 +10,15 @@ 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.appttude.h_mal.atlas_weather.ui.home.BaseActivity
import com.appttude.h_mal.atlas_weather.ui.settings.UnitSettingsActivity
import com.appttude.h_mal.atlas_weather.monoWeather.ui.settings.UnitSettingsActivity
import com.appttude.h_mal.atlas_weather.utils.goBack
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.android.synthetic.main.activity_main_navigation.*
private const val PERMISSION_LOCATION = 500
class MainActivity : BaseActivity() {
lateinit var navHost: NavHostFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_navigation)
@@ -25,7 +26,7 @@ class MainActivity : BaseActivity() {
val navView: BottomNavigationView = findViewById(R.id.nav_view)
setSupportActionBar(toolbar)
val navHost = supportFragmentManager
navHost = supportFragmentManager
.findFragmentById(R.id.container) as NavHostFragment
val navController = navHost.navController
navController.setGraph(R.navigation.main_navigation)
@@ -34,6 +35,7 @@ class MainActivity : BaseActivity() {
}
private fun setupBottomBar(navView: BottomNavigationView, navController: NavController) {
val tabs = setOf(R.id.nav_home, R.id.nav_world)
val appBarConfiguration = AppBarConfiguration(tabs)
@@ -58,6 +60,7 @@ class MainActivity : BaseActivity() {
startActivity(i)
return true
}
android.R.id.home -> onBackPressed()
}
return super.onOptionsItemSelected(item)
}

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.ui
package com.appttude.h_mal.atlas_weather.monoWeather.ui
import android.os.Bundle
import android.view.LayoutInflater
@@ -8,7 +8,9 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.WeatherRecyclerAdapter
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_main.*

View File

@@ -0,0 +1,45 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.details
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
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
* create an instance of this fragment.
*/
class FurtherInfoFragment : Fragment() {
private var param1: Forecast? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
param1 = FurtherInfoFragmentArgs.fromBundle(requireArguments()).forecast
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.activity_further_info, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
maxtemp.text = param1?.mainTemp
averagetemp.text = param1?.averageTemp
minimumtemp.text = param1?.minorTemp
windtext.text = param1?.windText
preciptext.text = param1?.precipitation
humiditytext.text = param1?.humidity
uvtext.text = param1?.uvi
sunrisetext.text = param1?.sunrise
sunsettext.text = param1?.sunset
}
}

View File

@@ -0,0 +1,102 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
import com.appttude.h_mal.atlas_weather.monoWeather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.WeatherRecyclerAdapter
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.HomeFragmentDirections
import com.appttude.h_mal.atlas_weather.utils.displayToast
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.activity_add_forecast.*
import kotlinx.android.synthetic.main.activity_add_forecast.progressBar
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_main.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
/**
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class HomeFragment : BaseFragment(), KodeinAware {
override val kodein by kodein()
private val factory by instance<ApplicationViewModelFactory>()
private val viewModel by activityViewModels<MainViewModel> { factory }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
@SuppressLint("MissingPermission")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerAdapter = WeatherRecyclerAdapter {
val directions =
HomeFragmentDirections.actionHomeFragmentToFurtherDetailsFragment(it)
navigateTo(directions)
}
forecast_listview.apply {
layoutManager = LinearLayoutManager(context)
adapter = recyclerAdapter
}
getPermissionResult(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION_PERMISSION_REQUEST){
viewModel.fetchData()
}
swipe_refresh.apply {
setOnRefreshListener {
getPermissionResult(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION_PERMISSION_REQUEST){
viewModel.fetchData()
}
isRefreshing = true
}
}
viewModel.weatherLiveData.observe(viewLifecycleOwner) {
recyclerAdapter.addCurrent(it)
}
viewModel.operationState.observe(viewLifecycleOwner, progressBarStateObserver(progressBar))
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
viewModel.operationState.observe(viewLifecycleOwner){
swipe_refresh.isRefreshing = false
}
}
@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) {
viewModel.fetchData()
displayToast("Permission granted")
} else {
displayToast("Permission denied")
}
}
}
}

View File

@@ -0,0 +1,6 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)

View File

@@ -0,0 +1,26 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.utils.loadImage
class ViewHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
var locationTV: TextView = listItemView.findViewById(R.id.location_main_4)
var conditionTV: TextView = listItemView.findViewById(R.id.condition_main_4)
var weatherIV: ImageView = listItemView.findViewById(R.id.icon_main_4)
var avgTempTV: TextView = listItemView.findViewById(R.id.temp_main_4)
var tempUnit: TextView = listItemView.findViewById(R.id.temp_unit_4)
fun bindData(weather: WeatherDisplay?){
locationTV.text = weather?.location
conditionTV.text = weather?.description
weatherIV.loadImage(weather?.iconURL, 64, 64)
avgTempTV.text = weather?.averageTemp?.toInt().toString()
tempUnit.text = weather?.unit
}
}

View File

@@ -0,0 +1,112 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
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.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.forecast.ViewHolderForecast
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.forecastDaily.ViewHolderForecastDaily
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.further.ViewHolderFurtherDetails
import com.appttude.h_mal.atlas_weather.utils.generateView
class WeatherRecyclerAdapter(
private val itemClick: (Forecast) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var weather: WeatherDisplay? = null
fun addCurrent(current: WeatherDisplay){
weather = current
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (getDataType(viewType)){
is ViewType.Empty -> {
val emptyViewHolder = View(parent.context)
EmptyViewHolder(emptyViewHolder)
}
is ViewType.Current -> {
val viewCurrent = parent.generateView(R.layout.mono_item_one)
ViewHolderCurrent(viewCurrent)
}
is ViewType.Forecast -> {
val viewForecast = parent.generateView(R.layout.mono_item_forecast)
ViewHolderForecast(viewForecast, itemClick)
}
is ViewType.Further -> {
val viewFurther = parent.generateView(R.layout.mono_item_two)
ViewHolderFurtherDetails(viewFurther)
}
is ViewType.ForecastDaily -> {
val viewForecast = parent.generateView(R.layout.list_item_forecast)
ViewHolderForecastDaily(viewForecast)
}
}
}
sealed class ViewType{
object Empty : ViewType()
object Current : ViewType()
object Forecast : ViewType()
object ForecastDaily : ViewType()
object Further : ViewType()
}
private fun getDataType(type: Int): ViewType {
return when (type){
0 -> ViewType.Empty
1 -> ViewType.Current
2 -> ViewType.Forecast
3 -> ViewType.Further
4 -> ViewType.ForecastDaily
else -> ViewType.Empty
}
}
override fun getItemViewType(position: Int): Int {
if (weather == null) return 0
return when(position){
0 -> 1
1 -> 3
2 -> 2
in 3 until (itemCount - 1) -> 4
else -> 0
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getDataType(getItemViewType(position))){
is ViewType.Empty -> {
holder as EmptyViewHolder
}
is ViewType.Current -> {
val viewHolderCurrent = holder as ViewHolderCurrent
viewHolderCurrent.bindData(weather)
}
is ViewType.Further -> {
val viewHolderCurrent = holder as ViewHolderFurtherDetails
viewHolderCurrent.bindData(weather)
}
is ViewType.Forecast -> {
val viewHolderForecast = holder as ViewHolderForecast
viewHolderForecast.bindView(weather?.forecast)
}
is ViewType.ForecastDaily -> {
val viewHolderForecast = holder as ViewHolderForecastDaily
weather?.forecast?.getOrNull(position -3)?.let {
viewHolderForecast.bindView(it)
}
}
}
}
override fun getItemCount(): Int {
if (weather == null) return 0
return 3 + (weather?.forecast?.size ?: 0)
}
}

View File

@@ -0,0 +1,24 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.forecast
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
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.utils.loadImage
class GridCellHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var dayTV: TextView = itemView.findViewById(R.id.widget_item_day)
var weatherIV: ImageView = itemView.findViewById(R.id.widget_item_image)
var mainTempTV: TextView = itemView.findViewById(R.id.widget_item_temp_high)
var lowTempTV: TextView = itemView.findViewById(R.id.widget_item_temp_low)
fun bindView(forecast: Forecast?) {
dayTV.text = forecast?.date
weatherIV.loadImage(forecast?.weatherIcon, 64, 64)
mainTempTV.text = forecast?.mainTemp
lowTempTV.text = forecast?.minorTemp
}
}

View File

@@ -0,0 +1,38 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.forecast
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
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.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.ViewHolderCurrent
import com.appttude.h_mal.atlas_weather.utils.generateView
class GridForecastAdapter(
val itemClick: (Forecast) -> Unit
): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
var weather: MutableList<Forecast> = mutableListOf()
fun addCurrent(current: List<Forecast>?){
weather.clear()
current?.let { weather.addAll(it) }
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val viewCurrent = parent.generateView(R.layout.mono_forecast_grid_item)
return GridCellHolder(viewCurrent)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val view = holder as GridCellHolder
val forecast = weather[position]
view.bindView(forecast)
view.itemView.setOnClickListener {
itemClick.invoke(forecast)
}
}
override fun getItemCount(): Int = weather.size
}

View File

@@ -0,0 +1,24 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.forecast
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
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.utils.loadImage
class ViewHolderForecast(
itemView: View,
private val itemClick: (Forecast) -> Unit
) : RecyclerView.ViewHolder(itemView) {
var recyclerView: RecyclerView = itemView.findViewById(R.id.forecast_recyclerview)
fun bindView(forecasts: List<Forecast>?) {
val adapter = GridForecastAdapter(itemClick)
adapter.addCurrent(forecasts)
recyclerView.adapter = adapter
}
}

View File

@@ -0,0 +1,24 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.forecastDaily
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
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.utils.loadImage
class ViewHolderForecastDaily(itemView: View) : RecyclerView.ViewHolder(itemView) {
var dateTV: TextView = itemView.findViewById(R.id.list_date)
var dayTV: TextView = itemView.findViewById(R.id.list_day)
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
var mainTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
fun bindView(forecast: Forecast?) {
dateTV.text = forecast?.date
dayTV.text = forecast?.day
weatherIV.loadImage(forecast?.weatherIcon, 64, 64)
mainTempTV.text = forecast?.mainTemp
}
}

View File

@@ -0,0 +1,26 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.further
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.utils.generateView
import kotlinx.android.synthetic.monoWeather.mono_item_two_cell.view.*
class GridAdapter(
context: Context,
list: List<Pair<Int, String>>
) : ArrayAdapter<Pair<Int, String>>(context, 0, list){
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: parent.generateView(R.layout.mono_item_two_cell)
val item = getItem(position)
return view.apply {
mono_item_cell.setImageResource(item!!.first)
mono_text_cell.text = item.second
}
}
}

View File

@@ -0,0 +1,21 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.further
import android.view.View
import android.widget.GridView
import androidx.recyclerview.widget.RecyclerView
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
class ViewHolderFurtherDetails(itemView: View) : RecyclerView.ViewHolder(itemView) {
var grid: GridView = itemView.findViewById(R.id.grid_mono)
fun bindData(weather: WeatherDisplay?){
grid.adapter = GridAdapter(itemView.context, listOf<Pair<Int, String>>(
Pair(R.drawable.breeze,"${weather?.windSpeed ?: "0"} km"),
Pair(R.drawable.water_drop,"${weather?.precipitation ?: "0"} %" ),
Pair(R.drawable.cloud_symbol,"${weather?.clouds ?: "0"} %" )
))
}
}

View File

@@ -0,0 +1,87 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.settings
import android.app.AlarmManager
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle
import android.preference.PreferenceActivity
import android.preference.PreferenceFragment
import androidx.preference.PreferenceManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.monoWeather.widget.NewAppWidget
import java.util.*
class UnitSettingsActivity : PreferenceActivity() {
private var prefListener: OnSharedPreferenceChangeListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
PreferenceManager.setDefaultValues(this, R.xml.prefs_screen, false)
fragmentManager.beginTransaction().replace(android.R.id.content, MyPreferenceFragment()).commit()
//listener on changed sort order preference:
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
prefListener = OnSharedPreferenceChangeListener { _, key ->
if (key == "temp_units") {
val intent = Intent(baseContext, NewAppWidget::class.java)
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
val ids = AppWidgetManager.getInstance(application).getAppWidgetIds(ComponentName(application, NewAppWidget::class.java))
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
sendBroadcast(intent)
}
// if (key == "notif_boolean") {
// setupNotificationBroadcaster(baseContext)
// }
if (key == "widget_black_background"){
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
val widgetManager = AppWidgetManager.getInstance(this)
val ids = widgetManager.getAppWidgetIds(ComponentName(this, NewAppWidget::class.java))
AppWidgetManager.getInstance(this).notifyAppWidgetViewDataChanged(ids, R.id.whole_widget_view)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
sendBroadcast(intent)
}
}
prefs.registerOnSharedPreferenceChangeListener(prefListener)
}
// fun setupNotificationBroadcaster(context: Context) {
// val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// val notificationIntent = Intent(context, NotificationReceiver::class.java)
// val broadcast = PendingIntent.getBroadcast(context, 100, notificationIntent,
// PendingIntent.FLAG_UPDATE_CURRENT)
// val cal: Calendar = Calendar.getInstance()
// cal.set(Calendar.HOUR_OF_DAY, 6)
// cal.set(Calendar.MINUTE, 8)
// cal.set(Calendar.SECOND, 5)
// alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.timeInMillis, AlarmManager.INTERVAL_DAY, broadcast)
// }
override fun onBackPressed() {
super.onBackPressed()
}
// @Override
// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
// Log.i(TAG, "onSharedPreferenceChanged: " + s);
// if (s == "temp_units"){
// Intent intent = new Intent(getBaseContext(), NewAppWidget.class);
// intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
//
// int[] ids = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), NewAppWidget.class));
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
// sendBroadcast(intent);
// }
// }
class MyPreferenceFragment : PreferenceFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.prefs_screen)
}
}
}

View File

@@ -0,0 +1,56 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.world
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.observe
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.utils.displayToast
import com.appttude.h_mal.atlas_weather.utils.goBack
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import kotlinx.android.synthetic.main.activity_add_forecast.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
class AddLocationFragment : BaseFragment(), KodeinAware {
override val kodein by kodein()
private val factory by instance<ApplicationViewModelFactory>()
private val viewModel by viewModels<WorldViewModel> { factory }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.activity_add_forecast, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
submit.setOnClickListener {
val locationName = location_name_tv.text?.trim()?.toString()
if (locationName.isNullOrBlank()){
location_name_tv.error = "Location cannot be blank"
return@setOnClickListener
}
viewModel.fetchDataForSingleLocation(locationName)
}
viewModel.operationState.observe(viewLifecycleOwner, progressBarStateObserver(progressBar))
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
viewModel.operationComplete.observe(viewLifecycleOwner) {
it?.getContentIfNotHandled()?.let {message ->
displayToast(message)
}
goBack()
}
}
}

View File

@@ -0,0 +1,85 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.world
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
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.world.WorldFragmentDirections
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.android.synthetic.main.fragment_add_location.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
/**
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class WorldFragment : BaseFragment(), KodeinAware {
override val kodein by kodein()
private val factory by instance<ApplicationViewModelFactory>()
val viewModel by viewModels<WorldViewModel> { factory }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment__two, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerAdapter = WorldRecyclerAdapter({
val direction =
WorldFragmentDirections.actionWorldFragmentToWorldItemFragment(it)
navigateTo(direction)
}){
MaterialAlertDialogBuilder(requireContext())
.setTitle("Delete weather?")
.setMessage("Are you sure want to delete $it?")
.setPositiveButton("Yes"){ _, _ ->
viewModel.deleteLocation(it)
}
.setNegativeButton("No", null)
.create()
.show()
}
world_recycler.apply {
layoutManager = LinearLayoutManager(context)
adapter = recyclerAdapter
}
viewModel.weatherLiveData.observe(viewLifecycleOwner) {
recyclerAdapter.addCurrent(it)
}
floatingActionButton.setOnClickListener{
navigateTo(R.id.action_worldFragment_to_addLocationFragment)
}
viewModel.operationState.observe(viewLifecycleOwner, progressBarStateObserver(progressBar2))
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
}
override fun onResume() {
super.onResume()
viewModel.fetchAllLocations()
}
}

View File

@@ -0,0 +1,102 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.world
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.utils.generateView
import com.appttude.h_mal.atlas_weather.utils.loadImage
class WorldRecyclerAdapter(
val itemClick: (WeatherDisplay) -> Unit,
val itemLongClick: (String) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var weather: MutableList<WeatherDisplay> = mutableListOf()
fun addCurrent(current: List<WeatherDisplay>){
weather.clear()
weather.addAll(current)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (getDataType(viewType)){
is ViewType.Empty -> {
val emptyViewHolder = View(parent.context)
EmptyViewHolder(emptyViewHolder)
}
is ViewType.Current -> {
val viewCurrent = parent.generateView(R.layout.db_list_item)
WorldHolderCurrent(viewCurrent)
}
}
}
sealed class ViewType{
object Empty : ViewType()
object Current : ViewType()
}
private fun getDataType(type: Int): ViewType {
return when (type){
0 -> ViewType.Empty
1 -> ViewType.Current
else -> ViewType.Empty
}
}
override fun getItemViewType(position: Int): Int {
return if (weather.isEmpty()) 0 else 1
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getDataType(getItemViewType(position))){
is ViewType.Empty -> {
holder as EmptyViewHolder
}
is ViewType.Current -> {
val viewHolderCurrent = holder as WorldHolderCurrent
val currentWeather = weather[position]
viewHolderCurrent.bindData(currentWeather)
viewHolderCurrent.itemView.setOnClickListener {
itemClick.invoke(weather[position])
}
viewHolderCurrent.itemView.setOnLongClickListener {
currentWeather.location?.let { location -> itemLongClick.invoke(location) }
true
}
}
}
}
override fun getItemCount(): Int {
return if (weather.size == 0) 1 else weather.size
}
internal class WorldHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
var locationTV: TextView = listItemView.findViewById(R.id.db_location)
var conditionTV: TextView = listItemView.findViewById(R.id.db_condition)
var weatherIV: ImageView = listItemView.findViewById(R.id.db_icon)
var avgTempTV: TextView = listItemView.findViewById(R.id.db_main_temp)
var tempUnit: TextView = listItemView.findViewById(R.id.db_minor_temp)
fun bindData(weather: WeatherDisplay?){
locationTV.text = weather?.location
conditionTV.text = weather?.description
weatherIV.loadImage(weather?.iconURL, 64, 64)
avgTempTV.text = weather?.forecast?.get(0)?.mainTemp
tempUnit.text = weather?.unit
}
}
internal class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
}

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.widget
package com.appttude.h_mal.atlas_weather.monoWeather.widget
import android.app.Activity
import android.app.PendingIntent
@@ -16,7 +16,7 @@ abstract class BaseWidgetClass : AppWidgetProvider(){
return RemoteViews(context.packageName, id)
}
fun createUpdatePendingIntent(context: Context, appWidgetId: Int): PendingIntent? {
fun AppWidgetProvider.createUpdatePendingIntent(context: Context, appWidgetId: Int): PendingIntent? {
val seconds = (System.currentTimeMillis() / 1000L).toInt()
val intentUpdate = Intent(context, this::class.java)
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE

View File

@@ -0,0 +1,78 @@
package com.appttude.h_mal.atlas_weather.monoWeather.widget
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.util.Log
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.LateInitKodein
import org.kodein.di.generic.instance
class MyWidgetRemoteViewsFactory(
private val context: Context,
val intent: Intent
) : RemoteViewsFactory{
private val TAG = "MyWidgetRemoteViewsFactory"
private val kodein = LateInitKodein()
private val helper : ServicesHelper by kodein.instance()
private var appWidgetId: Int? = 0
private var list: List<InnerWidgetData>? = null
init {
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID)
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
}
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 = 1
override fun getItemId(i: Int): Long = i.toLong()
override fun hasStableIds(): Boolean {
return true
}
}

View File

@@ -0,0 +1,141 @@
package com.appttude.h_mal.atlas_weather.monoWeather.widget
import android.Manifest
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.net.Uri
import android.util.Log
import android.widget.RemoteViews
import androidx.core.app.ActivityCompat
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.WidgetData
import com.appttude.h_mal.atlas_weather.monoWeather.ui.MainActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.kodein.di.KodeinAware
import org.kodein.di.LateInitKodein
import org.kodein.di.generic.instance
/**
* Implementation of App Widget functionality.
*/
private val TAG = NewAppWidget::class.java.simpleName
class NewAppWidget : BaseWidgetClass() {
private val kodein = LateInitKodein()
private val helper : ServicesHelper by kodein.instance()
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
// There may be multiple widgets active, so update all of them
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return
}
CoroutineScope(Dispatchers.IO).launch {
val results = helper.fetchData()
if (results) return@launch
val weatherWidgetCurrent = helper.getWidgetWeather()
withContext(Dispatchers.Main){
for (appWidgetId in appWidgetIds) {
val updatePendingIntent = createUpdatePendingIntent(context, appWidgetId)
val views = createRemoteView(context, R.layout.new_app_widget)
bindView(context, appWidgetId, views, updatePendingIntent, weatherWidgetCurrent)
}
super.onUpdate(context, appWidgetManager, appWidgetIds)
}
}
}
override fun onEnabled(context: Context) {
try {
val appWidgetManager = AppWidgetManager.getInstance(context)
val thisAppWidget = ComponentName(context.packageName, NewAppWidget::class.java.name)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
onUpdate(context, appWidgetManager, appWidgetIds)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_listview)
} catch (e: Exception) {
Log.e(TAG, "onEnabled: ", e)
}
}
override fun onDisabled(context: Context) {
// Enter relevant functionality for when the last widget is disabled
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action ==
AppWidgetManager.ACTION_APPWIDGET_UPDATE) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val thisAppWidget = ComponentName(context.packageName, NewAppWidget::class.java.name)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_listview)
}
super.onReceive(context, intent)
}
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))
}
}
private fun bindView(
context: Context,
appWidgetId: Int,
views: RemoteViews,
updatePendingIntent: PendingIntent?,
weather: WidgetData?){
val appWidgetManager = AppWidgetManager.getInstance(context)
views.setInt(R.id.whole_widget_view, "setBackgroundColor", helper.getWidgetBackground())
weather?.let {
val intent = createForecastListIntent(
context,
appWidgetId
)
views.setRemoteAdapter(R.id.widget_listview, intent)
views.setTextViewText(R.id.widget_main_temp, it.currentTemp)
views.setTextViewText(R.id.widget_feel_temp, "°C")
views.setTextViewText(R.id.widget_current_location, it.location)
views.setImageViewResource(R.id.location_icon, R.drawable.location_flag)
views.setImageViewBitmap(R.id.widget_current_icon, it.icon)
val clickPendingIntentTemplate = createClickingPendingIntent(context, MainActivity::class.java)
views.setPendingIntentTemplate(R.id.widget_listview, clickPendingIntentTemplate)
views.setOnClickPendingIntent(R.id.widget_current_icon, updatePendingIntent)
views.setOnClickPendingIntent(R.id.widget_current_location, updatePendingIntent)
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
return
}
Log.i(TAG, "onPostExecute: weather is empty")
views.setTextViewText(R.id.widget_current_location, "Refresh")
views.setImageViewResource(R.id.widget_current_icon, R.drawable.widget_error_icon)
views.setImageViewResource(R.id.location_icon, R.drawable.refreshing)
views.setOnClickPendingIntent(R.id.widget_current_icon, updatePendingIntent)
views.setOnClickPendingIntent(R.id.widget_current_location, updatePendingIntent)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}

View File

@@ -0,0 +1,10 @@
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)
}
}

View File

@@ -0,0 +1,15 @@
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)
}

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108"
android:tint="#FFFFFF">
<group android:scaleX="2.61"
android:scaleY="2.61"
android:translateX="22.68"
android:translateY="22.68">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
</group>
</vector>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_marginLeft="24dp"
android:layout_marginTop="6dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="6dp"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/list_icon"
android:layout_width="64dp"
android:layout_height="64dp"
tools:src="@drawable/day_305" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="15dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/list_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Wednesday" />
<TextView
android:id="@+id/list_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
tools:text="Nov 28" />
</LinearLayout>
<LinearLayout
android:id="@+id/list_temp_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginRight="4dp"
android:orientation="horizontal">
<TextView
android:id="@+id/list_main_temp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:includeFontPadding="false"
android:textSize="28sp"
tools:text="28" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="°" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widget_item_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/widget_item_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:includeFontPadding="false"
android:textColor="#ffffff"
tools:text="Dec 1" />
<ImageView
android:id="@+id/widget_item_image"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center"
tools:src="@drawable/demo_icon" />
<TextView
android:id="@+id/widget_item_temp_high"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:includeFontPadding="false"
android:gravity="center"
android:textColor="#ffffff"
android:textSize="12sp"
android:textStyle="bold"
tools:text="20" />
<TextView
android:id="@+id/widget_item_temp_low"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:includeFontPadding="false"
android:gravity="center"
android:textColor="#ffffff"
android:textSize="12sp"
tools:text="11" />
</LinearLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginBottom="24dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/forecast_recyclerview"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/widget_item"
android:orientation="horizontal"
tools:itemCount="24"
app:spanCount="1"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/loc_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:orientation="horizontal"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginRight="12dp"
android:tint="@color/colorAccent"
app:srcCompat="@drawable/maps_and_flags" />
<TextView
android:id="@+id/location_main_4"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="22sp"
tools:text="Greater London" />
</LinearLayout>
<TextView
android:id="@+id/temp_main_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="60sp"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/loc_container"
tools:text="22" />
<TextView
android:id="@+id/temp_unit_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/degrees_c"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@id/temp_main_4"
app:layout_constraintTop_toTopOf="@id/temp_main_4" />
<ImageView
android:id="@+id/icon_main_4"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="12dp"
android:tint="@color/colorAccent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/temp_main_4"
app:srcCompat="@drawable/cloud_symbol" />
<TextView
android:id="@+id/condition_main_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/icon_main_4"
android:textSize="16sp"
tools:text="windy with rain" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="24dp"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<GridView
android:id="@+id/grid_mono"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/mono_item_two_cell"
android:numColumns="3"
android:gravity="center"
android:stretchMode="columnWidth"
tools:layout_height="88dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/mono_item_cell"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:src="@drawable/cloud_symbol"
android:padding="6dp"
style="@style/icon_style__further_deatils" />
<TextView
android:id="@+id/mono_text_cell"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="15 km"
android:textSize="16sp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/mono_item_cell"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More