diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4c96378b --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +/src/main/res/raw/config.properties + +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..0e40fe8f --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ + +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..74fa738b --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Daresay Weather \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..88ea3aa1 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,122 @@ + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 00000000..5cd135a0 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..85226533 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + 1.8 + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 00000000..7f68460d --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index b3fe3b4f..0ee7a56a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ +So, a bit overworked, but it was such good fun! + +To run, add file config.properties with key and api-key below in res/raw and compile. + +api-key=62fc... + +Started with a tab ambition but time ran out, so it's just one fragment in a tab control. However, it's prepared for more.... + +Enjoy! + + + + +--- + + +---- # Work sample - Application developer ## Assignment diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..f8a72110 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,31 @@ +/build +/src/main/res/raw/config.properties + + +# Built application files +/*/build/ + +# Crashlytics configuations +com_crashlytics_export_strings.xml + +# Local configuration file (sdk path, etc) +local.properties + +# Gradle generated files +.gradle/ + +# Signing files +.signing/ + +# User-specific configurations +.idea/libraries/ +.idea/workspace.xml +.idea/tasks.xml +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/modules.xml +.idea/scopes/scope_settings.xml +*.iml \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 00000000..7e3176d4 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,52 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.3" + + defaultConfig { + applicationId "se.bynumbers.daresayweather" + minSdkVersion 26 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + vectorDrawables.useSupportLibrary = true + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + compileOptions { + kotlinOptions { jvmTarget = "1.8" } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + //This version might not be correct, + implementation 'org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0' + + implementation 'com.android.volley:volley:1.1.1' + implementation 'com.google.code.gson:gson:2.8.6' + + + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.drawerlayout:drawerlayout:1.0.0' + implementation 'com.google.android.gms:play-services-location:17.0.0' + + implementation 'androidx.core:core-ktx:1.2.0' + implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/se/bynumbers/sampleweather/ExampleInstrumentedTest.kt b/app/src/androidTest/java/se/bynumbers/sampleweather/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..155d1e61 --- /dev/null +++ b/app/src/androidTest/java/se/bynumbers/sampleweather/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package se.bynumbers.sampleweather + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("se.bynumbers.myapplication", appContext.packageName) + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c54f151b --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/MainActivity.kt b/app/src/main/java/se/bynumbers/daresayweather/MainActivity.kt new file mode 100644 index 00000000..09a06483 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/MainActivity.kt @@ -0,0 +1,57 @@ +package se.bynumbers.daresayweather + +import android.Manifest +import android.content.DialogInterface +import android.content.pm.PackageManager +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.viewpager.widget.ViewPager +import com.google.android.material.tabs.TabLayout +import kotlinx.serialization.json.Json.Default.context +import se.bynumbers.daresayweather.ui.main.SectionsPagerAdapter +import se.bynumbers.daresayweather.util.HttpHelper +import se.bynumbers.daresayweather.R + + +class MainActivity : AppCompatActivity() { + private val TAG = "MainActivity" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + val sectionsPagerAdapter = + SectionsPagerAdapter( + this, + supportFragmentManager + ) + val viewPager: ViewPager = findViewById(R.id.view_pager) + viewPager.adapter = sectionsPagerAdapter + val tabs: TabLayout = findViewById(R.id.tabs) + tabs.setupWithViewPager(viewPager) + + + /* val fab: FloatingActionButton = findViewById(R.id.fab) + + fab.setOnClickListener { view -> + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) + .setAction("Action", null).show() + } + + */ + + } + + + override fun onStop() { + super.onStop() + HttpHelper.getInstance(applicationContext).cancelAll() + } + private fun toast(msg : String){ + Toast.makeText(this@MainActivity, msg, Toast.LENGTH_SHORT).show() + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/model/CurrentWeather.kt b/app/src/main/java/se/bynumbers/daresayweather/model/CurrentWeather.kt new file mode 100644 index 00000000..e1400e07 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/model/CurrentWeather.kt @@ -0,0 +1,66 @@ +package se.bynumbers.daresayweather.model + +import android.app.Application +import android.content.Context +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.android.volley.Response +import com.android.volley.VolleyError +import com.google.gson.Gson +import com.google.gson.JsonObject +import kotlinx.serialization.json.Json +import se.bynumbers.daresayweather.util.WeatherDataHelper + +import java.lang.reflect.Array + + +data class Coord ( + var lon : Double = 0.0, + var lat : Double = 0.0 +) + +class Weather ( + var id : Int = 0, + var main: String? = null, + var description: String? = null, + var icon: String? = null +) + + +class Wind ( + var speed : Double = 0.0, + var deg : Int = 0 +) +data class Clouds ( + var all : Int = 0 +) + +data class Sys ( + var type : Int = 0, + var id : Int = 0, + var message : Double = 0.0, + var country: String? = null, + var sunrise : Int = 0, + var sunset : Int = 0, + var pod: String? = null +) + +data class CurrentWeather ( + var coord: Coord? = null, + var weather: String? = null, + var base: String? = null, + var main: Main? = null, + var visibility : Int = 0, + var wind: Wind? = null, + var clouds: Clouds? = null, + var dt : Int = 0, + var sys: Sys? = null, + var timezone : Int = 0, + var id : Int = 0, + var name: String? = null, + var cod : Int = 0 +) + diff --git a/app/src/main/java/se/bynumbers/daresayweather/model/Forecast.kt b/app/src/main/java/se/bynumbers/daresayweather/model/Forecast.kt new file mode 100644 index 00000000..435bf7d2 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/model/Forecast.kt @@ -0,0 +1,57 @@ +package se.bynumbers.daresayweather.model +import kotlinx.serialization.* + +@Serializable +class Main( + var sea_level: Double = 0.0, + var grnd_level : Double = 0.0, + var temp_kf : Double = 0.0, + var temp : Double = 0.0, + var feels_like : Double = 0.0, + var temp_min : Double = 0.0, + var temp_max : Double = 0.0, + var pressure : Int = 0, + var humidity : Int = 0 + +) + + +class Rain ( + @SerialName("3h") + var threeh : Double = 0.0 +) + +class Snow +{ + @SerialName("3h") + var threeh = 0.0 +} + +class List( + var dt : Int = 0, + var main : Main? = null, + var weather : List? = null, + var clouds : Clouds? = null, + var wind : Wind? = null, + var sys : Sys? = null, + var dt_txt : String? = null, + var rain : Rain? = null, + var snow : Snow? = null +) + + + +data class City( + var id: Int = 0, + var name : String? = null, + var coord : Coord? = null, + var country : String? = null +) + +class Forecast( + var cod : String? = null, + var message : Double = 0.0, + var cnt : Int = 0, + var list : List, + var city: City? = null +) \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/model/LocationData.kt b/app/src/main/java/se/bynumbers/daresayweather/model/LocationData.kt new file mode 100644 index 00000000..f84686eb --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/model/LocationData.kt @@ -0,0 +1,106 @@ +package se.bynumbers.daresayweather.model + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Application +import android.content.Context +import android.content.pm.PackageManager +import android.location.Location +import android.util.Log +import android.widget.Toast +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import com.google.android.gms.location.LocationCallback +import com.google.android.gms.location.LocationRequest +import com.google.android.gms.location.LocationResult +import com.google.android.gms.location.LocationServices +import se.bynumbers.daresayweather.util.PermissionStatusListener +import se.bynumbers.daresayweather.R + +data class LocationData( + val longitude: Double, + val latitude: Double +) +class LocationLiveData(context: Context) : LiveData() { + private val TAG = "LocationLiveData" + var _context = context + private var fusedLocationClient = LocationServices.getFusedLocationProviderClient(context) + companion object { + + val locationRequest: LocationRequest = LocationRequest.create().apply { + interval = 10000 + fastestInterval = 5000 + priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY + } + } + private val locationCallback = object : LocationCallback() { + override fun onLocationResult(locationResult: LocationResult?) { + locationResult ?: return + for (location in locationResult.locations) { + setLocationData(location) + } + } + } + override fun onInactive() { + super.onInactive() + fusedLocationClient.removeLocationUpdates(locationCallback) + } + + override fun onActive() { + super.onActive() + fusedLocationClient.lastLocation + .addOnSuccessListener { location: Location? -> + location?.also { + setLocationData(it) + } + } + .addOnFailureListener { error -> + Log.e(TAG, "Could not get last location: ${error.message}") + } + startLocationUpdates() + + } + + + private fun startLocationUpdates() { + if(checkLocationPermission()) { + Log.i(TAG, "Start location updates ") + fusedLocationClient.requestLocationUpdates( + locationRequest, + locationCallback, + null + ) + } else { + Toast.makeText(_context, R.string.no_location_check, Toast.LENGTH_SHORT).show() + } + } + private fun setLocationData(location: Location) { + Log.i(TAG, "Setting location: ${location.longitude}, ${location.latitude}") + value = LocationData( + longitude = location.longitude, + latitude = location.latitude + ) + } + private fun checkLocationPermission(): Boolean { + return ContextCompat.checkSelfPermission( + _context, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission( + _context, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + } + +} +class LocationListener(application : Application) : AndroidViewModel(application) { + + private var locationData = LocationLiveData(application) + + fun getLocationData() = locationData + + val locationPermissionStatusLiveData = PermissionStatusListener(application.applicationContext, + Manifest.permission.ACCESS_FINE_LOCATION) +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/ui/main/CurrentWeatherFragment.kt b/app/src/main/java/se/bynumbers/daresayweather/ui/main/CurrentWeatherFragment.kt new file mode 100644 index 00000000..5f3f5fd2 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/ui/main/CurrentWeatherFragment.kt @@ -0,0 +1,263 @@ +package se.bynumbers.daresayweather.ui.main + +import android.Manifest +import android.app.Activity +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import kotlinx.android.synthetic.main.fragment_current_weather.* + +import se.bynumbers.daresayweather.model.LocationListener +import se.bynumbers.daresayweather.util.DateTimeHelper +import se.bynumbers.daresayweather.util.LocationHelper +import se.bynumbers.daresayweather.util.PermissionStatus +import se.bynumbers.daresayweather.util.ResourceHelper +import se.bynumbers.daresayweather.R + + +/** + * A placeholder fragment containing a simple view. + */ +class CurrentWeatherFragment : Fragment() { + + private lateinit var pageViewModel: PageCurrentWeatherViewModel + private lateinit var locationDataListener : LocationListener + private lateinit var currentWeatherViewModel : CurrentWeatherViewModel + private var alertDialog: AlertDialog? = null + private val TAG = "CurrentWeatherFragment" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + locationDataListener = ViewModelProvider(this).get(LocationListener::class.java) + pageViewModel = ViewModelProvider(this).get(PageCurrentWeatherViewModel::class.java) + currentWeatherViewModel = ViewModelProvider(this).get(CurrentWeatherViewModel::class.java) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val root = inflater.inflate(R.layout.fragment_current_weather, container, false) + val statusTextView = root.findViewById(R.id.status_label) + val currentTextView = root.findViewById(R.id.current_label) + var mainImageView = root.findViewById(R.id.main_weather_image) + val temperatureView = root.findViewById(R.id.temperature_label) + val unitSwitch = root.findViewById(R.id.unit_switch) + val locationSwitch = root.findViewById(R.id.location_switch) + val btnRetry = root.findViewById(R.id.btn_retry) + + btnRetry.setVisibility(View.INVISIBLE) + mainImageView.setImageDrawable(requireContext().getDrawable(R.drawable.wi_na)) + temperatureView.text = "" + statusTextView.text = getString(R.string.no_location_check) + btnRetry.setOnClickListener { + if(LocationHelper.checkLocationPermission(requireContext())){ + + startLocationUpdates() + updateLocationUI(true) + } + + } + locationSwitch.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { _, isChecked -> + if (isChecked) { + if(askForLocationPermission(requireActivity())==1) { + startLocationUpdates() + } + } else { + stopLocationUpdates() + } + }) + unitSwitch.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { _, isChecked -> + ResourceHelper.setFahrenheit(isChecked, requireContext()) + var currentTemp : String = temperatureView.text.toString() + if(isChecked){ + currentTemp = ResourceHelper.toFahrenheit(currentTemp) + } else { + currentTemp = ResourceHelper.toCelsius(currentTemp) + } + temperatureView.text = "$currentTemp ${ResourceHelper.getFahrenheitOrCelsius(requireContext())}" + + }) + + //TODO: Refactor and probably not needed + locationDataListener.locationPermissionStatusLiveData.observe(this.viewLifecycleOwner, Observer { + if(it is PermissionStatus.Denied) { + Log.d(TAG,"Show location permission dialog") + if(askForLocationPermission(requireActivity())==1){ + startLocationUpdates() + } + + } else { + + } + + Log.d(TAG,"Permission status is $it") + }) + + + + + + pageViewModel.weatherImage.observe(this.viewLifecycleOwner, Observer { + mainImageView.setImageDrawable(it) + }) + pageViewModel.text.observe(this.viewLifecycleOwner, Observer { + statusTextView.text = it + }) + pageViewModel.current_label.observe(this.viewLifecycleOwner, Observer { + currentTextView.text = it + }) + pageViewModel.temperature.observe(this.viewLifecycleOwner, Observer { + temperatureView.text = "$it ${ResourceHelper.getFahrenheitOrCelsius(requireContext())}" + }) + /* pageViewModel.degreeLabel.observe(this.viewLifecycleOwner, Observer { + degreeView.setImageDrawable(it) + }) + + */ + return root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val locationEnabled = LocationHelper.checkLocationPermission(requireContext()) + updateLocationUI(locationEnabled) + updateBackground() + startLocationUpdates() + } + + fun askForLocationPermission(activity: Activity) : Int { + if (alertDialog?.isShowing == true) { + return -1 // null or dialog already being shown + } + if (!LocationHelper.checkLocationPermission(activity)) { + Log.d(TAG, "No permission given, showing dialog") + if (shouldShowRequestPermissionRationale( + Manifest.permission.ACCESS_FINE_LOCATION + ) + ) { + alertDialog = AlertDialog.Builder(activity) + .setMessage(requireContext().getString(R.string.ask_use_permission)) + .setPositiveButton(android.R.string.ok) { _, _ -> + requestPermissions( + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + LocationHelper.PERMISSIONS_LOCATION + ) + } + .setNegativeButton(android.R.string.cancel, null).create() + alertDialog?.apply { + show() + } + } else { + + requestPermissions( + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + LocationHelper.PERMISSIONS_LOCATION + ) + + } + return 0 //wait + } else { + Log.d(TAG, "Permission given, not showing dialog") + return 1 + } + } + + + + private fun startLocationUpdates(){ + locationDataListener.getLocationData().removeObservers(this.viewLifecycleOwner) + locationDataListener.getLocationData().observe(this.viewLifecycleOwner, Observer { + Log.d(TAG, "Location data aquired: ${it.latitude}, ${it.longitude}") + pageViewModel.updateLocation(it) + currentWeatherViewModel.getCurrentWeather(requireContext(), it).observe(this.viewLifecycleOwner, Observer { + Log.d(TAG, "Weather data aquired: $it") + pageViewModel.setWeatherData(it) + // latLong.text = getString(R.string.latLong, it.longitude, it.latitude) + + }) + updateBackground() + }) + } + private fun stopLocationUpdates(){ + locationDataListener.getLocationData().removeObservers(this.viewLifecycleOwner) + } + private fun updateBackground(){ + +//This function will change background drawable, so place it where you want. +//This function will change background drawable, so place it where you want. + var id : Int + if(DateTimeHelper.isDayTime()) id = R.drawable.bg_day else id = R.drawable.bg_night + constraintLayout.background = requireContext().getDrawable(id) + } + private fun updateLocationUI(on : Boolean){ + if(view != null) { + var statusView = view!!.findViewById(R.id.status_label) + var text = ""//""Location data is allowed" + if(!on){ + text = getString(R.string.no_location_check) + val btnRetry = view!!.findViewById(R.id.btn_retry) + btnRetry.setVisibility(View.VISIBLE) + } else { + val btnRetry = view!!.findViewById(R.id.btn_retry) + btnRetry.setVisibility(View.INVISIBLE) + } + statusView.text = text + val locationSwitch = view!!.findViewById(R.id.location_switch) + locationSwitch.isChecked = on + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + Log.d(TAG,"onRequestPermissionsResult") + if(LocationHelper.arePermissionsResultYes(requestCode, + permissions, grantResults, requireContext() + )) { + Log.d(TAG,"Location permission granted") + startLocationUpdates() + updateLocationUI(true) + + } else { + Log.d(TAG,"Location permission not granted") + stopLocationUpdates() + updateLocationUI(false) + } + + } + + + companion object { + /** + * The fragment argument representing the section number for this + * fragment. + */ + private const val ARG_SECTION_NUMBER = "section_number" + + /** + * Returns a new instance of this fragment for the given section + * number. + */ + @JvmStatic + fun newInstance(sectionNumber: Int): CurrentWeatherFragment { + return CurrentWeatherFragment().apply { + arguments = Bundle().apply { + putInt(ARG_SECTION_NUMBER, sectionNumber) + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/ui/main/CurrentWeatherViewModel.kt b/app/src/main/java/se/bynumbers/daresayweather/ui/main/CurrentWeatherViewModel.kt new file mode 100644 index 00000000..543e7240 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/ui/main/CurrentWeatherViewModel.kt @@ -0,0 +1,40 @@ +package se.bynumbers.daresayweather.ui.main + +import android.app.Application +import android.content.Context +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import com.android.volley.Response +import com.android.volley.VolleyError +import com.google.gson.Gson +import com.google.gson.JsonObject +import se.bynumbers.daresayweather.model.LocationData +import se.bynumbers.daresayweather.util.WeatherDataHelper + +class CurrentWeatherLiveData : MutableLiveData(), Response.Listener, + Response.ErrorListener { + + override fun onResponse(response: JsonObject?) { + value = response + } + + override fun onErrorResponse(error: VolleyError?) { + TODO("Not yet implemented") + } + +} + +class CurrentWeatherViewModel(application: Application) : AndroidViewModel(application) { + private val TAG = "CurrentWeatherViewModel" + private var currentWeatherData = MutableLiveData() + + //TODO Singleton + private val wdHelper = WeatherDataHelper() + + fun getCurrentWeather(context: Context, locationData: LocationData) : MutableLiveData { + currentWeatherData = wdHelper.getForecast(context, locationData) + return currentWeatherData + } + // + +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/ui/main/ForecastWeatherFragment.kt b/app/src/main/java/se/bynumbers/daresayweather/ui/main/ForecastWeatherFragment.kt new file mode 100644 index 00000000..d75102e0 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/ui/main/ForecastWeatherFragment.kt @@ -0,0 +1,58 @@ +package se.bynumbers.daresayweather.ui.main + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer + +import se.bynumbers.daresayweather.R + +/** + * A placeholder fragment containing a simple view. + */ +//TODO +class ForecastWeatherFragment : Fragment() { + + private lateinit var pageViewModel: PageForecastViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val root = inflater.inflate(R.layout.fragment_forecast, container, false) + val textView: TextView = root.findViewById(R.id.status_label) + pageViewModel.text.observe(this.viewLifecycleOwner, Observer { + textView.text = it + }) + return root + } + + companion object { + /** + * The fragment argument representing the section number for this + * fragment. + */ + private const val ARG_SECTION_NUMBER = "section_number" + + /** + * Returns a new instance of this fragment for the given section + * number. + */ + @JvmStatic + fun newInstance(sectionNumber: Int): ForecastWeatherFragment { + return ForecastWeatherFragment().apply { + arguments = Bundle().apply { + putInt(ARG_SECTION_NUMBER, sectionNumber) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/ui/main/PageCurrentWeatherViewModel.kt b/app/src/main/java/se/bynumbers/daresayweather/ui/main/PageCurrentWeatherViewModel.kt new file mode 100644 index 00000000..3bb1cd30 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/ui/main/PageCurrentWeatherViewModel.kt @@ -0,0 +1,98 @@ +package se.bynumbers.daresayweather.ui.main + +import android.app.Application +import android.graphics.drawable.Drawable +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonObject + +import se.bynumbers.daresayweather.model.LocationData +import se.bynumbers.daresayweather.model.Main +import se.bynumbers.daresayweather.model.Weather +import se.bynumbers.daresayweather.util.Constants +import se.bynumbers.daresayweather.util.DateTimeHelper +import se.bynumbers.daresayweather.util.ResourceHelper +import se.bynumbers.daresayweather.R +import kotlin.math.roundToInt + + +class PageCurrentWeatherViewModel(application : Application) : AndroidViewModel(application) { + private val TAG = "PageCurrentWeatherViewModel" + private val _index = MutableLiveData() + private val _currentWeatherImage = MutableLiveData() + private val _currentWeather = + CurrentWeatherLiveData() + private val _currentPlace = MutableLiveData() + private var _latestLocation = + LocationData(0.0, 0.0) + + //TODO: Gör en klass, plocka ut element för element CurrentWeather + val text: LiveData = Transformations.map(_currentPlace) { + // Log.d("PgeCurrentWeatherViewModel","Calling getForecast") + //WeatherHelper.getForecast(application.applicationContext, it) + //_currentWeather.getCurrentWeather(application.applicationContext, it) + if(it == null){ + application.applicationContext.getString(R.string.ask_use_permission) + } else { + "" + } + } + + val current_label: LiveData = Transformations.map(_currentWeather) { + + it["name"].asString + } + /* val degreeLabel : LiveData = Transformations.map(_currentWeather) { + val _context = application.applicationContext + var degreeDrawable : Drawable? = null + degreeDrawable = ResourceHelper.getFahrenheitOrCelsius(_context) + degreeDrawable + }*/ + val weatherImage: LiveData = Transformations.map(_currentWeather) { + val _context = application.applicationContext + var weatherDrawable : Drawable? + weatherDrawable = ResourceHelper.getWeatherDrawableFromMap(_context, getWeatherIconKey(it)) + weatherDrawable + } + val temperature: LiveData = Transformations.map(_currentWeather) { + + val gson = Gson() + //TODO Fugly, use TypeToken instead + val jo : JsonObject = it["main"].asJsonObject + val main : Main = gson.fromJson(jo, Main::class.java) + main.temp.roundToInt().toString() + } + private fun getWeatherIconKey(jo:JsonObject) : String { + val gson = Gson() + //TOOO Fix with TypeToken plz + val ja : JsonArray = jo["weather"].asJsonArray + val weatherMeta : Weather = gson.fromJson(ja.get(0).asJsonObject, Weather::class.java) + + var dayOrNight = if(DateTimeHelper.isDayTime()) "d" else "n" + var key = Constants.weatherIconMap["${weatherMeta.id}$dayOrNight"] + if(key == null){ + key = "wi_na" + } + return key + } + fun setWeatherData(currentWeather: JsonObject?){ + _currentWeather.value = currentWeather + } + fun updateLocation(locationData : LocationData){ + if(!locationData.equals(_latestLocation)) { + _currentPlace.value = locationData + _latestLocation = locationData + } + } + fun setIndex(index: Int) { + _index.value = index + } + fun setImage(drawable: Drawable){ + _currentWeatherImage.value = drawable + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/ui/main/PageForecastViewModel.kt b/app/src/main/java/se/bynumbers/daresayweather/ui/main/PageForecastViewModel.kt new file mode 100644 index 00000000..ce0b59f6 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/ui/main/PageForecastViewModel.kt @@ -0,0 +1,23 @@ +package se.bynumbers.daresayweather.ui.main + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations +import androidx.lifecycle.ViewModel +import se.bynumbers.daresayweather.model.CurrentWeather +//TODO +class PageForecastViewModel : ViewModel() { + + private val _index = MutableLiveData() + private val _currentWeather = MutableLiveData() + val text: LiveData = Transformations.map(_index) { + "Hello world from section: $it" + } + val currentPlace: LiveData = Transformations.map(_currentWeather) { + "${it.name}" + } + fun setIndex(index: Int) { + _index.value = index + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/ui/main/SectionsPagerAdapter.kt b/app/src/main/java/se/bynumbers/daresayweather/ui/main/SectionsPagerAdapter.kt new file mode 100644 index 00000000..d78006de --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/ui/main/SectionsPagerAdapter.kt @@ -0,0 +1,37 @@ +package se.bynumbers.daresayweather.ui.main + +import android.content.Context +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentPagerAdapter +import se.bynumbers.daresayweather.R + +private val TAB_TITLES = arrayOf( + R.string.tab_text_1, + R.string.tab_text_2 +) + +/** + * A [FragmentPagerAdapter] that returns a fragment corresponding to + * one of the sections/tabs/pages. + */ +class SectionsPagerAdapter(private val context: Context, fm: FragmentManager) + : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { + + override fun getItem(position: Int): Fragment { + // getItem is called to instantiate the fragment for the given page. + // Return a PlaceholderFragment (defined as a static inner class below). + return CurrentWeatherFragment.newInstance( + position + 1 + ) + } + + override fun getPageTitle(position: Int): CharSequence? { + return context.resources.getString(TAB_TITLES[position]) + } + + override fun getCount(): Int { + // Show 2 total pages. + return 1 + } +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/Constants.kt b/app/src/main/java/se/bynumbers/daresayweather/util/Constants.kt new file mode 100644 index 00000000..ee85fd02 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/Constants.kt @@ -0,0 +1,126 @@ +package se.bynumbers.daresayweather.util + +class Constants { + //weather.id (from Open->icon + companion object { + const val WEATHER_URL = "https://worksample-api.herokuapp.com/weather" + const val SP_DARESAY_WEATHER = "SP_DARESAY_WEATHER" + const val SP_LOCATION_ENABLED = "SP_LOCATION_ENABLED" + const val SP_FAHRENHEIT = "SP_FAHRENHEIT" + const val API_KEY = "api-key" + val weatherIconMap: HashMap = hashMapOf( + "201d" to "wi_day_thunderstorm", + "201n" to "wi_night_alt_thunderstorm", + "202d" to "wi_day_thunderstorm", + "202n" to "wi_night_alt_thunderstorm", + "210d" to "wi_day_thunderstorm", + "210n" to "wi_night_alt_thunderstorm", + "211d" to "wi_day_thunderstorm", + "211n" to "wi_night_alt_thunderstorm", + "212d" to "wi_day_thunderstorm", + "212n" to "wi_night_alt_thunderstorm", + "221d" to "wi_day_thunderstorm", + "221d" to "wi_night_alt_thunderstorm", + "230d" to "wi_day_thunderstorm", + "230n" to "wi_night_alt_thunderstorm", + "231d" to "wi_day_thunderstorm", + "231n" to "wi_night_alt_thunderstorm", + "232d" to "wi_day_thunderstorm", + "232n" to "wi_night_alt_thunderstorm", + + "300d" to "wi_day_sprinkle", + "300n" to "wi_night_sprinkle", + "301d" to "wi_day_sprinkle", + "301n" to "wi_night_sprinkle", + "302d" to "wi_day_rain", + "302n" to "wi_night_rain", + "310d" to "wi_day_rain", + "310n" to "wi_night_rain", + "311d" to "wi_day_rain", + "311n" to "wi_night_rain", + "312d" to "wi_day_rain", + "312n" to "wi_night_rain", + "313d" to "wi_day_showers", + "313n" to "wi_night_showers", + "314d" to "wi_day_showers", + "314n" to "wi_night_showers", + "321d" to "wi_rain", + "321n" to "wi_night_rain", + + "500d" to "wi_day_sprinkle", + "500n" to "wi_night_sprinkle", + "501d" to "wi_day_sprinkle", + "501n" to "wi_night_sprinkle", + "502d" to "wi_rain", + "502n" to "wi_night_rain", + "503d" to "wi_rain", + "503n" to "wi_night_rain", + "504d" to "wi_rain", + "504n" to "wi_night_rain", + "511d" to "wi_hail", + "511n" to "wi_night_hail", + "520d" to "wi_day_showers", + "520n" to "wi_night_showers", + "521d" to "wi_day_showers", + "522n" to "wi_night_showers", + "531d" to "wi_day_showers", + "531n" to "wi_night_showers", + + "600d" to "wi_day_snow", + "600n" to "wi_night_alt_snow", + "601d" to "wi_day_snow", + "601n" to "wi_night_alt_snow", + "602d" to "wi_day_snow", + "602n" to "wi_night_alt_snow", + "611d" to "wi_day_sleet", + "611n" to "wi_night_alt_sleet", + "612d" to "wi_day_sleet", + "612n" to "wi_night_alt_sleet", + "613d" to "wi_day_sleet", + "613n" to "wi_night_alt_sleet", + "615d" to "wi_day_sleet", + "615n" to "wi_night_alt_sleet", + "616d" to "wi_day_sleet", + "616n" to "wi_night_alt_sleet", + "620d" to "wi_day_snow", + "620n" to "wi_night_alt_snow", + "621d" to "wi_day_snow", + "621n" to "wi_night_alt_snow", + "622d" to "wi_day_snow", + "622n" to "wi_night_alt_snow", + + "701d" to "wi_fog", + "701n" to "wi_night_fog", + "711d" to "wi_fog", + "711n" to "wi_night_fog", + "721d" to "wi_fog", + "721n" to "wi_night_fog", + "731d" to "wi_fog", + "731n" to "wi_night_fog", + "741d" to "wi_fog", + "741n" to "wi_night_fog", + "751d" to "wi_fog", + "751n" to "wi_night_fog", + "761d" to "wi_fog", + "761n" to "wi_night_fog", + "762d" to "wi_fog", + "762n" to "wi_night_fog", + "771d" to "wi_fog", + "771n" to "wi_night_fog", + "781d" to "wi_day_rain_wind", + "781n" to "wi_night_rain_wind", + + "800d" to "wi_day_sunny", + "800n" to "wi_night_clear", + "801d" to "wi_day_cloudy_high", + "801n" to "wi_night_cloudy_high", + "802d" to "wi_day_cloudy_high", + "802n" to "wi_night_cloudy_high", + "803d" to "wi_day_cloudy", + "803n" to "wi_night_cloudy", + "804d" to "wi_day_sunny_overcast", + "804n" to "wi_night_cloudy_high", + "na" to "wi_na" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/DateTimeHelper.kt b/app/src/main/java/se/bynumbers/daresayweather/util/DateTimeHelper.kt new file mode 100644 index 00000000..9cb56601 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/DateTimeHelper.kt @@ -0,0 +1,16 @@ +package se.bynumbers.daresayweather.util + + +import java.time.LocalDateTime + +class DateTimeHelper { + companion object { + fun isDayTime() : Boolean { + val now : LocalDateTime = LocalDateTime.now() + if(now.hour > 6 && now.hour < 18){ + return true + } + return false + } + } +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/HttpHelper.kt b/app/src/main/java/se/bynumbers/daresayweather/util/HttpHelper.kt new file mode 100644 index 00000000..9c355d9a --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/HttpHelper.kt @@ -0,0 +1,68 @@ +package se.bynumbers.daresayweather.util + +import android.content.Context +import android.util.Log +import android.widget.Toast +import com.android.volley.* + +import com.android.volley.toolbox.JsonObjectRequest +import com.android.volley.toolbox.Volley +//import com.android.volley.toolbox. +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import kotlinx.serialization.PrimitiveKind +import se.bynumbers.daresayweather.model.CurrentWeather +//import se.bynumbers.daresayweather.model.CurrentWeatherHelper + +import se.bynumbers.daresayweather.R + + +val url = "http://my-json-feed" + +class HttpHelper private constructor(context: Context) { + init { + // Init using context argument + } + + companion object : SingletonHolder(::HttpHelper) + val TAG = "HttpHelper" + + val requestQueue: RequestQueue by lazy { + // applicationContext is key, it keeps you from leaking the + // Activity or BroadcastReceiver if someone passes one in. + Volley.newRequestQueue(context.applicationContext) + } + + fun addToRequestQueue(req: Request) { + requestQueue.add(req) + } + + fun getCurrentWeatherJson(context : Context, url: String, callback: WeatherDataHelper) { + val gson = Gson() + val jsonObjectRequest = JsonObjectRequest( + Request.Method.GET, url, null, + Response.Listener { response -> + + Log.d(TAG, "Response: ${response}") + val retJo = gson.fromJson(response.toString(), JsonObject::class.java) + callback.onResponse(retJo) + + }, + Response.ErrorListener { error -> + Log.e(TAG, "Network error: ${error.message}") + //retJo = null + //TODO: On ERROR + Toast.makeText(context, context.getString(R.string.check_network), Toast.LENGTH_SHORT ).show() + } + ) + + + this.addToRequestQueue(jsonObjectRequest) + + } + fun cancelAll() { + requestQueue.cancelAll{true} + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/LocationHelper.kt b/app/src/main/java/se/bynumbers/daresayweather/util/LocationHelper.kt new file mode 100644 index 00000000..1685c067 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/LocationHelper.kt @@ -0,0 +1,80 @@ +package se.bynumbers.daresayweather.util + +import android.Manifest +import android.app.Activity +import android.app.Application +import android.content.Context +import android.content.pm.PackageManager +import android.util.Log +import androidx.appcompat.app.AlertDialog +import androidx.core.app.ActivityCompat +import androidx.core.app.ActivityCompat.requestPermissions +import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale +import androidx.core.content.ContextCompat +import se.bynumbers.daresayweather.util.Constants.Companion.SP_DARESAY_WEATHER +import se.bynumbers.daresayweather.util.Constants.Companion.SP_LOCATION_ENABLED +import se.bynumbers.daresayweather.R + +class LocationHelper { + + companion object { + private val TAG = "LocationHelper" + const val PERMISSIONS_LOCATION = 1001 + fun checkLocationPermission(context : Context): Boolean { + return ContextCompat.checkSelfPermission( + context, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + /*&& ActivityCompat.checkSelfPermission( + context, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED*/ + } + fun arePermissionsResultYes( + requestCode: Int, + permissions: Array, + grantResults: IntArray, + context: Context + ) : Boolean { + val sp = context.getSharedPreferences(SP_DARESAY_WEATHER, + Context.MODE_PRIVATE + ) + when (requestCode) { + PERMISSIONS_LOCATION -> { + if (grantResults.isNotEmpty() + && grantResults[0] == PackageManager.PERMISSION_GRANTED + ) { + + // permission was granted, yay! Do the + // location-related task you need to do. + if (ContextCompat.checkSelfPermission( + context, + Manifest.permission.ACCESS_FINE_LOCATION + ) + == PackageManager.PERMISSION_GRANTED + ) { + + val editor = sp.edit() + editor.putBoolean(SP_LOCATION_ENABLED, true) + editor.apply() + + return true + } + } + + } + } + val editor = sp.edit() + editor.putBoolean(SP_LOCATION_ENABLED, false) + editor.apply() + + return false + + } + + + + + } + +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/PermissionStatusListener.kt b/app/src/main/java/se/bynumbers/daresayweather/util/PermissionStatusListener.kt new file mode 100644 index 00000000..d811c654 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/PermissionStatusListener.kt @@ -0,0 +1,30 @@ +package se.bynumbers.daresayweather.util + +import android.content.Context +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.lifecycle.LiveData +import se.bynumbers.daresayweather.R + +class PermissionStatusListener(private val context: Context, + private val permissionToListen: String) : LiveData() { + + override fun onActive() = handlePermissionCheck() + + private fun handlePermissionCheck() { + val isPermissionGranted = ContextCompat.checkSelfPermission(context, + permissionToListen) == PackageManager.PERMISSION_GRANTED + + if (isPermissionGranted) + postValue(PermissionStatus.Granted()) + else + postValue(PermissionStatus.Denied()) + } +} + +sealed class PermissionStatus { + data class Granted(val message: Int = R.string.permission_status_granted) : PermissionStatus() + data class Denied(val message: Int = R.string.permission_status_denied) : PermissionStatus() + data class Blocked(val message: Int = R.string.permission_status_blocked) : PermissionStatus() +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/ResourceHelper.kt b/app/src/main/java/se/bynumbers/daresayweather/util/ResourceHelper.kt new file mode 100644 index 00000000..e609dd62 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/ResourceHelper.kt @@ -0,0 +1,85 @@ +package se.bynumbers.daresayweather.util + +import android.content.Context +import android.content.Context.MODE_PRIVATE +import android.content.SharedPreferences +import android.graphics.drawable.Drawable +import android.util.Log +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.text.isDigitsOnly +import se.bynumbers.daresayweather.util.Constants.Companion.SP_DARESAY_WEATHER +import se.bynumbers.daresayweather.util.Constants.Companion.SP_FAHRENHEIT +import se.bynumbers.daresayweather.R +import kotlin.math.roundToInt + +class ResourceHelper { + + companion object { + private val TAG = "ResourceHelper" + fun getWeatherDrawableFromMap(context: Context, key: String): Drawable? { + + Log.d(TAG, "Looking for resource with name: $key") + val r = context.resources + + val packageName = context.packageName + val i = r.getIdentifier(key, "drawable", packageName) + + return AppCompatResources.getDrawable(context, i) + + + } + fun setFahrenheit(on : Boolean, context : Context){ + val sp = context.getSharedPreferences(SP_DARESAY_WEATHER, + MODE_PRIVATE + ) + val editor = sp.edit() + editor.putBoolean(SP_FAHRENHEIT, on) + editor.apply() + + } + fun toFahrenheit(temp : String) : String { + if(temp.isEmpty()) return "0" + val temp1 = temp.substringBefore(" ") + + //TODO check for digits only (but note minus degre + if(temp1.isEmpty()) return "0" + val fahrenheit = (temp1.toDouble() * 9)/5 + 32 + + return fahrenheit.roundToInt().toString() + } + fun toCelsius(temp : String) : String { + if(temp.isEmpty()) return "0" + val temp1 = temp.substringBefore(" ") + if(temp1.isEmpty() || !temp1.isDigitsOnly()) return "0" + val celsius = ((temp1.toDouble()-32) * 5)/9 + + return celsius.roundToInt().toString() + + } + + fun getFahrenheitOrCelsius(context: Context): String? { + + + /* val r = context.resources + + val packageName = context.packageName + val sp = context.getSharedPreferences("SP_DARESAY_WEATHER", MODE_PRIVATE) + val fahrenheit = sp.getBoolean("SP_FARENHEIT", false) + + var id = R.drawable.wi_celsius + if (fahrenheit) { + id = R.drawable.wi_fahrenheit + } + Log.d(TAG, "Farenheit or celsius: $id") + return AppCompatResources.getDrawable(context, id) +*/ val sp = context.getSharedPreferences(SP_DARESAY_WEATHER, MODE_PRIVATE) + val fahrenheit = sp.getBoolean(SP_FAHRENHEIT, false) + + var ret ="\u00B0C" + if (fahrenheit) { + ret = "\u00B0F" + } + return ret + } + } +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/SingletonHolder.kt b/app/src/main/java/se/bynumbers/daresayweather/util/SingletonHolder.kt new file mode 100644 index 00000000..33a88d21 --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/SingletonHolder.kt @@ -0,0 +1,38 @@ +package se.bynumbers.daresayweather.util + +// Convenience class that you only want to write once... +// Use with, for example: +// class HttpHelper private constructor(context: Context) { +// init { +// // Init using context argument +// } +// +// companion object : SingletonHolder(::HttpHelper) +//} +// And then +// HttpHelper.getInstance(context).doStuff() + + +open class SingletonHolder(creator: (A) -> T) { + private var creator: ((A) -> T)? = creator + @Volatile private var instance: T? = null + + fun getInstance(arg: A): T { + val i = instance + if (i != null) { + return i + } + + return synchronized(this) { + val i2 = instance + if (i2 != null) { + i2 + } else { + val created = creator!!(arg) + instance = created + creator = null + created + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/se/bynumbers/daresayweather/util/WeatherHelper.kt b/app/src/main/java/se/bynumbers/daresayweather/util/WeatherHelper.kt new file mode 100644 index 00000000..693b5e5e --- /dev/null +++ b/app/src/main/java/se/bynumbers/daresayweather/util/WeatherHelper.kt @@ -0,0 +1,69 @@ +package se.bynumbers.daresayweather.util + +import android.content.Context +import android.content.res.Resources +import android.util.Log +import android.widget.Toast +import androidx.lifecycle.LiveData + +import androidx.lifecycle.MutableLiveData +import com.android.volley.Response +import com.android.volley.VolleyError +import com.google.gson.Gson +import com.google.gson.JsonObject + +import se.bynumbers.daresayweather.model.LocationData +import se.bynumbers.daresayweather.util.Constants.Companion.WEATHER_URL +import se.bynumbers.daresayweather.R +import java.io.InputStream +import java.util.* + +//TODO Should refactor this to listener...when time. +class WeatherDataHelper : MutableLiveData(), Response.Listener, + + Response.ErrorListener { + var weatherData = MutableLiveData() + + override fun onErrorResponse(error: VolleyError?) { + + } + + override fun onResponse(response: JsonObject) { + //if(response CurrentWeather){ + //TODO!! Change type here (Fix Json->Pojo + weatherData.value = response + } + fun getForecast(context: Context, locationData : LocationData) : MutableLiveData { + + val apiKey = getApiKey(context) + + val url = + "${WEATHER_URL}?lat=${locationData.latitude}&lon=${locationData.longitude}&key=$apiKey" + HttpHelper.getInstance(context).getCurrentWeatherJson(context, url, this) + return weatherData + } + + private fun getApiKey( + context: Context + ): String? { + + val resources: Resources = context.resources + val TAG = "WeatherHelper" + //TODO: Cache api-key in SecurePrefs? + try { + val rawResource: InputStream = resources.openRawResource(R.raw.config) + val properties = Properties() + properties.load(rawResource) + return properties.getProperty(Constants.API_KEY) + } catch (e: Exception) { + Log.e(TAG, "Unable to aquire api-key: " + e.message) + Toast.makeText(context, context.getString(R.string.no_config_file), Toast.LENGTH_SHORT ).show() + } + return "" + } +} + + + + + diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_day.png b/app/src/main/res/drawable/bg_day.png new file mode 100644 index 00000000..e652579b Binary files /dev/null and b/app/src/main/res/drawable/bg_day.png differ diff --git a/app/src/main/res/drawable/bg_night.png b/app/src/main/res/drawable/bg_night.png new file mode 100644 index 00000000..fe07208f Binary files /dev/null and b/app/src/main/res/drawable/bg_night.png differ diff --git a/app/src/main/res/drawable/daresay_logo.png b/app/src/main/res/drawable/daresay_logo.png new file mode 100644 index 00000000..f243ad58 Binary files /dev/null and b/app/src/main/res/drawable/daresay_logo.png differ diff --git a/app/src/main/res/drawable/daresay_logo1024.png b/app/src/main/res/drawable/daresay_logo1024.png new file mode 100644 index 00000000..38817e94 Binary files /dev/null and b/app/src/main/res/drawable/daresay_logo1024.png differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..a0ad202f --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/wi_alien.xml b/app/src/main/res/drawable/wi_alien.xml new file mode 100644 index 00000000..668dbb02 --- /dev/null +++ b/app/src/main/res/drawable/wi_alien.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_celsius.xml b/app/src/main/res/drawable/wi_celsius.xml new file mode 100644 index 00000000..5d05632d --- /dev/null +++ b/app/src/main/res/drawable/wi_celsius.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_cloud.xml b/app/src/main/res/drawable/wi_cloud.xml new file mode 100644 index 00000000..f9e527cc --- /dev/null +++ b/app/src/main/res/drawable/wi_cloud.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_cloudy.xml b/app/src/main/res/drawable/wi_cloudy.xml new file mode 100644 index 00000000..7c86114d --- /dev/null +++ b/app/src/main/res/drawable/wi_cloudy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_cloudy_gusts.xml b/app/src/main/res/drawable/wi_cloudy_gusts.xml new file mode 100644 index 00000000..624ae9a2 --- /dev/null +++ b/app/src/main/res/drawable/wi_cloudy_gusts.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_cloudy_windy.xml b/app/src/main/res/drawable/wi_cloudy_windy.xml new file mode 100644 index 00000000..bcbb11c8 --- /dev/null +++ b/app/src/main/res/drawable/wi_cloudy_windy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_cloudy.xml b/app/src/main/res/drawable/wi_day_cloudy.xml new file mode 100644 index 00000000..bff6d42a --- /dev/null +++ b/app/src/main/res/drawable/wi_day_cloudy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_cloudy_gusts.xml b/app/src/main/res/drawable/wi_day_cloudy_gusts.xml new file mode 100644 index 00000000..4a6f9e2a --- /dev/null +++ b/app/src/main/res/drawable/wi_day_cloudy_gusts.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_cloudy_high.xml b/app/src/main/res/drawable/wi_day_cloudy_high.xml new file mode 100644 index 00000000..de43fcca --- /dev/null +++ b/app/src/main/res/drawable/wi_day_cloudy_high.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_cloudy_windy.xml b/app/src/main/res/drawable/wi_day_cloudy_windy.xml new file mode 100644 index 00000000..cf949b12 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_cloudy_windy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_fog.xml b/app/src/main/res/drawable/wi_day_fog.xml new file mode 100644 index 00000000..d1fdc1bb --- /dev/null +++ b/app/src/main/res/drawable/wi_day_fog.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_hail.xml b/app/src/main/res/drawable/wi_day_hail.xml new file mode 100644 index 00000000..8f19a68a --- /dev/null +++ b/app/src/main/res/drawable/wi_day_hail.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_haze.xml b/app/src/main/res/drawable/wi_day_haze.xml new file mode 100644 index 00000000..4b62a504 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_haze.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_light_wind.xml b/app/src/main/res/drawable/wi_day_light_wind.xml new file mode 100644 index 00000000..a51cadd8 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_light_wind.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_lightning.xml b/app/src/main/res/drawable/wi_day_lightning.xml new file mode 100644 index 00000000..e84c8e2b --- /dev/null +++ b/app/src/main/res/drawable/wi_day_lightning.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_rain.xml b/app/src/main/res/drawable/wi_day_rain.xml new file mode 100644 index 00000000..3c43e14a --- /dev/null +++ b/app/src/main/res/drawable/wi_day_rain.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_rain_mix.xml b/app/src/main/res/drawable/wi_day_rain_mix.xml new file mode 100644 index 00000000..a3e9b58e --- /dev/null +++ b/app/src/main/res/drawable/wi_day_rain_mix.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_rain_wind.xml b/app/src/main/res/drawable/wi_day_rain_wind.xml new file mode 100644 index 00000000..6e232ef7 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_rain_wind.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_showers.xml b/app/src/main/res/drawable/wi_day_showers.xml new file mode 100644 index 00000000..7ee1496f --- /dev/null +++ b/app/src/main/res/drawable/wi_day_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_sleet.xml b/app/src/main/res/drawable/wi_day_sleet.xml new file mode 100644 index 00000000..3dba5009 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_sleet.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_sleet_storm.xml b/app/src/main/res/drawable/wi_day_sleet_storm.xml new file mode 100644 index 00000000..7a8461a7 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_sleet_storm.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_snow.xml b/app/src/main/res/drawable/wi_day_snow.xml new file mode 100644 index 00000000..178231e9 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_snow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_snow_thunderstorm.xml b/app/src/main/res/drawable/wi_day_snow_thunderstorm.xml new file mode 100644 index 00000000..aa643604 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_snow_thunderstorm.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_snow_wind.xml b/app/src/main/res/drawable/wi_day_snow_wind.xml new file mode 100644 index 00000000..6c8dcb50 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_snow_wind.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_sprinkle.xml b/app/src/main/res/drawable/wi_day_sprinkle.xml new file mode 100644 index 00000000..b4888520 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_sprinkle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_storm_showers.xml b/app/src/main/res/drawable/wi_day_storm_showers.xml new file mode 100644 index 00000000..cc0784b3 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_storm_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_sunny.xml b/app/src/main/res/drawable/wi_day_sunny.xml new file mode 100644 index 00000000..838787bf --- /dev/null +++ b/app/src/main/res/drawable/wi_day_sunny.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_sunny_overcast.xml b/app/src/main/res/drawable/wi_day_sunny_overcast.xml new file mode 100644 index 00000000..1549552f --- /dev/null +++ b/app/src/main/res/drawable/wi_day_sunny_overcast.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_thunderstorm.xml b/app/src/main/res/drawable/wi_day_thunderstorm.xml new file mode 100644 index 00000000..83a46c64 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_thunderstorm.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_day_windy.xml b/app/src/main/res/drawable/wi_day_windy.xml new file mode 100644 index 00000000..7fe17794 --- /dev/null +++ b/app/src/main/res/drawable/wi_day_windy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_degrees.xml b/app/src/main/res/drawable/wi_degrees.xml new file mode 100644 index 00000000..838e4aec --- /dev/null +++ b/app/src/main/res/drawable/wi_degrees.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_down.xml b/app/src/main/res/drawable/wi_direction_down.xml new file mode 100644 index 00000000..c48846bd --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_down_left.xml b/app/src/main/res/drawable/wi_direction_down_left.xml new file mode 100644 index 00000000..8d6af957 --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_down_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_down_right.xml b/app/src/main/res/drawable/wi_direction_down_right.xml new file mode 100644 index 00000000..4c088267 --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_down_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_left.xml b/app/src/main/res/drawable/wi_direction_left.xml new file mode 100644 index 00000000..c7c5f38e --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_right.xml b/app/src/main/res/drawable/wi_direction_right.xml new file mode 100644 index 00000000..4128d087 --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_up.xml b/app/src/main/res/drawable/wi_direction_up.xml new file mode 100644 index 00000000..ba79309f --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_up_left.xml b/app/src/main/res/drawable/wi_direction_up_left.xml new file mode 100644 index 00000000..048d3a78 --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_up_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_direction_up_right.xml b/app/src/main/res/drawable/wi_direction_up_right.xml new file mode 100644 index 00000000..244326f8 --- /dev/null +++ b/app/src/main/res/drawable/wi_direction_up_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_dust.xml b/app/src/main/res/drawable/wi_dust.xml new file mode 100644 index 00000000..7c25f685 --- /dev/null +++ b/app/src/main/res/drawable/wi_dust.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_fahrenheit.xml b/app/src/main/res/drawable/wi_fahrenheit.xml new file mode 100644 index 00000000..fccda550 --- /dev/null +++ b/app/src/main/res/drawable/wi_fahrenheit.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_fog.xml b/app/src/main/res/drawable/wi_fog.xml new file mode 100644 index 00000000..aa78fa7b --- /dev/null +++ b/app/src/main/res/drawable/wi_fog.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_hail.xml b/app/src/main/res/drawable/wi_hail.xml new file mode 100644 index 00000000..ea804c06 --- /dev/null +++ b/app/src/main/res/drawable/wi_hail.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_hot.xml b/app/src/main/res/drawable/wi_hot.xml new file mode 100644 index 00000000..5c24303d --- /dev/null +++ b/app/src/main/res/drawable/wi_hot.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_lightning.xml b/app/src/main/res/drawable/wi_lightning.xml new file mode 100644 index 00000000..ddfac178 --- /dev/null +++ b/app/src/main/res/drawable/wi_lightning.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_na.xml b/app/src/main/res/drawable/wi_na.xml new file mode 100644 index 00000000..96b86889 --- /dev/null +++ b/app/src/main/res/drawable/wi_na.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_cloudy.xml b/app/src/main/res/drawable/wi_night_alt_cloudy.xml new file mode 100644 index 00000000..acd7a864 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_cloudy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_cloudy_gusts.xml b/app/src/main/res/drawable/wi_night_alt_cloudy_gusts.xml new file mode 100644 index 00000000..47aab846 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_cloudy_gusts.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_cloudy_windy.xml b/app/src/main/res/drawable/wi_night_alt_cloudy_windy.xml new file mode 100644 index 00000000..d6fd8834 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_cloudy_windy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_hail.xml b/app/src/main/res/drawable/wi_night_alt_hail.xml new file mode 100644 index 00000000..b0b32386 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_hail.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_lightning.xml b/app/src/main/res/drawable/wi_night_alt_lightning.xml new file mode 100644 index 00000000..597fb460 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_lightning.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_partly_cloudy.xml b/app/src/main/res/drawable/wi_night_alt_partly_cloudy.xml new file mode 100644 index 00000000..00dfb9ae --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_partly_cloudy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_rain.xml b/app/src/main/res/drawable/wi_night_alt_rain.xml new file mode 100644 index 00000000..80643d00 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_rain.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_rain_mix.xml b/app/src/main/res/drawable/wi_night_alt_rain_mix.xml new file mode 100644 index 00000000..a964499b --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_rain_mix.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_rain_wind.xml b/app/src/main/res/drawable/wi_night_alt_rain_wind.xml new file mode 100644 index 00000000..38a4e48d --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_rain_wind.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_showers.xml b/app/src/main/res/drawable/wi_night_alt_showers.xml new file mode 100644 index 00000000..a45af3ef --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_sleet.xml b/app/src/main/res/drawable/wi_night_alt_sleet.xml new file mode 100644 index 00000000..296262d7 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_sleet.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_sleet_storm.xml b/app/src/main/res/drawable/wi_night_alt_sleet_storm.xml new file mode 100644 index 00000000..29c0b486 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_sleet_storm.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_snow.xml b/app/src/main/res/drawable/wi_night_alt_snow.xml new file mode 100644 index 00000000..457db3f7 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_snow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_snow_thunderstorm.xml b/app/src/main/res/drawable/wi_night_alt_snow_thunderstorm.xml new file mode 100644 index 00000000..fd35b9a1 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_snow_thunderstorm.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_sprinkle.xml b/app/src/main/res/drawable/wi_night_alt_sprinkle.xml new file mode 100644 index 00000000..f79e08a0 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_sprinkle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_storm_showers.xml b/app/src/main/res/drawable/wi_night_alt_storm_showers.xml new file mode 100644 index 00000000..fbf82484 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_storm_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_alt_thunderstorm.xml b/app/src/main/res/drawable/wi_night_alt_thunderstorm.xml new file mode 100644 index 00000000..6f30be66 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_alt_thunderstorm.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_clear.xml b/app/src/main/res/drawable/wi_night_clear.xml new file mode 100644 index 00000000..2de4fa85 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_clear.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_cloudy.xml b/app/src/main/res/drawable/wi_night_cloudy.xml new file mode 100644 index 00000000..2096581f --- /dev/null +++ b/app/src/main/res/drawable/wi_night_cloudy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_cloudy_gusts.xml b/app/src/main/res/drawable/wi_night_cloudy_gusts.xml new file mode 100644 index 00000000..7557cb77 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_cloudy_gusts.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_cloudy_high.xml b/app/src/main/res/drawable/wi_night_cloudy_high.xml new file mode 100644 index 00000000..0a556a75 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_cloudy_high.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_cloudy_windy.xml b/app/src/main/res/drawable/wi_night_cloudy_windy.xml new file mode 100644 index 00000000..31eb30e4 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_cloudy_windy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_fog.xml b/app/src/main/res/drawable/wi_night_fog.xml new file mode 100644 index 00000000..b6d757e8 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_fog.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_hail.xml b/app/src/main/res/drawable/wi_night_hail.xml new file mode 100644 index 00000000..ae400f76 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_hail.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_lightning.xml b/app/src/main/res/drawable/wi_night_lightning.xml new file mode 100644 index 00000000..6aca63bb --- /dev/null +++ b/app/src/main/res/drawable/wi_night_lightning.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_low_wind.xml b/app/src/main/res/drawable/wi_night_low_wind.xml new file mode 100644 index 00000000..dbeef91f --- /dev/null +++ b/app/src/main/res/drawable/wi_night_low_wind.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_partly_cloudy.xml b/app/src/main/res/drawable/wi_night_partly_cloudy.xml new file mode 100644 index 00000000..0a556a75 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_partly_cloudy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_rain.xml b/app/src/main/res/drawable/wi_night_rain.xml new file mode 100644 index 00000000..edd18f40 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_rain.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_rain_mix.xml b/app/src/main/res/drawable/wi_night_rain_mix.xml new file mode 100644 index 00000000..a44400f9 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_rain_mix.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_rain_wind.xml b/app/src/main/res/drawable/wi_night_rain_wind.xml new file mode 100644 index 00000000..6ac2ab72 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_rain_wind.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_showers.xml b/app/src/main/res/drawable/wi_night_showers.xml new file mode 100644 index 00000000..def54118 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_sprinkle.xml b/app/src/main/res/drawable/wi_night_sprinkle.xml new file mode 100644 index 00000000..2c2d0d5c --- /dev/null +++ b/app/src/main/res/drawable/wi_night_sprinkle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_night_storm_showers.xml b/app/src/main/res/drawable/wi_night_storm_showers.xml new file mode 100644 index 00000000..9a63eac1 --- /dev/null +++ b/app/src/main/res/drawable/wi_night_storm_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_rain.xml b/app/src/main/res/drawable/wi_rain.xml new file mode 100644 index 00000000..40906ac0 --- /dev/null +++ b/app/src/main/res/drawable/wi_rain.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_rain_mix.xml b/app/src/main/res/drawable/wi_rain_mix.xml new file mode 100644 index 00000000..b5ebc264 --- /dev/null +++ b/app/src/main/res/drawable/wi_rain_mix.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_refresh.xml b/app/src/main/res/drawable/wi_refresh.xml new file mode 100644 index 00000000..ce6e1ab0 --- /dev/null +++ b/app/src/main/res/drawable/wi_refresh.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_showers.xml b/app/src/main/res/drawable/wi_showers.xml new file mode 100644 index 00000000..031b9e1f --- /dev/null +++ b/app/src/main/res/drawable/wi_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_sleet.xml b/app/src/main/res/drawable/wi_sleet.xml new file mode 100644 index 00000000..e7320651 --- /dev/null +++ b/app/src/main/res/drawable/wi_sleet.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_snow.xml b/app/src/main/res/drawable/wi_snow.xml new file mode 100644 index 00000000..cc9ef7b2 --- /dev/null +++ b/app/src/main/res/drawable/wi_snow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_sprinkle.xml b/app/src/main/res/drawable/wi_sprinkle.xml new file mode 100644 index 00000000..667a8a7a --- /dev/null +++ b/app/src/main/res/drawable/wi_sprinkle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_stars.xml b/app/src/main/res/drawable/wi_stars.xml new file mode 100644 index 00000000..97362153 --- /dev/null +++ b/app/src/main/res/drawable/wi_stars.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_storm_showers.xml b/app/src/main/res/drawable/wi_storm_showers.xml new file mode 100644 index 00000000..b5f5925c --- /dev/null +++ b/app/src/main/res/drawable/wi_storm_showers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_sunrise.xml b/app/src/main/res/drawable/wi_sunrise.xml new file mode 100644 index 00000000..ef4158c6 --- /dev/null +++ b/app/src/main/res/drawable/wi_sunrise.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_sunset.xml b/app/src/main/res/drawable/wi_sunset.xml new file mode 100644 index 00000000..b0557004 --- /dev/null +++ b/app/src/main/res/drawable/wi_sunset.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/wi_thunderstorm.xml b/app/src/main/res/drawable/wi_thunderstorm.xml new file mode 100644 index 00000000..2b9235f5 --- /dev/null +++ b/app/src/main/res/drawable/wi_thunderstorm.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/font/libre_franklin_bold.ttf b/app/src/main/res/font/libre_franklin_bold.ttf new file mode 100644 index 00000000..4ed839d0 Binary files /dev/null and b/app/src/main/res/font/libre_franklin_bold.ttf differ diff --git a/app/src/main/res/font/libre_franklin_medium.ttf b/app/src/main/res/font/libre_franklin_medium.ttf new file mode 100644 index 00000000..4fe2cdbf Binary files /dev/null and b/app/src/main/res/font/libre_franklin_medium.ttf differ diff --git a/app/src/main/res/font/libre_franklin_regular.ttf b/app/src/main/res/font/libre_franklin_regular.ttf new file mode 100644 index 00000000..0bc60245 Binary files /dev/null and b/app/src/main/res/font/libre_franklin_regular.ttf differ diff --git a/app/src/main/res/layout-land/fragment_current_weather.xml b/app/src/main/res/layout-land/fragment_current_weather.xml new file mode 100644 index 00000000..0f4fef5e --- /dev/null +++ b/app/src/main/res/layout-land/fragment_current_weather.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..af0d0bf9 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_current_weather.xml b/app/src/main/res/layout/fragment_current_weather.xml new file mode 100644 index 00000000..1fec6e3d --- /dev/null +++ b/app/src/main/res/layout/fragment_current_weather.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forecast.xml b/app/src/main/res/layout/fragment_forecast.xml new file mode 100644 index 00000000..224edb0f --- /dev/null +++ b/app/src/main/res/layout/fragment_forecast.xml @@ -0,0 +1,35 @@ + + + + + +