diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..410d5594
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,267 @@
+
+# Created by https://www.gitignore.io/api/linux,macos,windows,android,androidstudio
+# Edit at https://www.gitignore.io/?templates=linux,macos,windows,android,androidstudio
+
+### Android ###
+# Built application files
+*.apk
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+release/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+# Android Studio 3 in .gitignore file.
+.idea/caches
+.idea/modules.xml
+# Comment next line if keeping position of elements in Navigation Editor is relevant for you
+.idea/navEditor.xml
+
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+vcs.xml
+
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/
+
+### Android Patch ###
+gen-external-apklibs
+output.json
+
+# Replacement of .externalNativeBuild directories introduced
+# with Android Studio 3.5.
+.cxx/
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+### AndroidStudio ###
+# Covers files to be ignored for android development using Android Studio.
+
+# Built application files
+
+# Files for the ART/Dalvik VM
+
+# Java class files
+
+# Generated files
+
+# Gradle files
+.gradle
+
+# Signing files
+.signing/
+
+# Local configuration file (sdk path, etc)
+
+# Proguard folder generated by Eclipse
+
+# Log Files
+
+# Android Studio
+/*/build/
+/*/local.properties
+/*/out
+/*/*/build
+/*/*/production
+*.ipr
+*.swp
+
+# Android Patch
+
+# External native build folder generated in Android Studio 2.2 and later
+
+# NDK
+obj/
+
+# IntelliJ IDEA
+*.iws
+/out/
+
+# User-specific configurations
+.idea/caches/
+.idea/libraries/
+.idea/shelf/
+.idea/.name
+.idea/compiler.xml
+.idea/copyright/profiles_settings.xml
+.idea/encodings.xml
+.idea/misc.xml
+.idea/scopes/scope_settings.xml
+.idea/vcs.xml
+.idea/jsLibraryMappings.xml
+.idea/datasources.xml
+.idea/dataSources.ids
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+
+# OS-specific files
+.DS_Store?
+
+# Legacy Eclipse project files
+.classpath
+.project
+.cproject
+.settings/
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.ear
+
+# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
+hs_err_pid*
+
+## Plugin-specific files:
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Mongo Explorer plugin
+.idea/mongoSettings.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+### AndroidStudio Patch ###
+
+!/gradle/wrapper/gradle-wrapper.jar
+
+# End of https://www.gitignore.io/api/linux,macos,windows,android,androidstudio
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..681f41ae
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 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/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/app/.gitignore b/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 00000000..9aab14e7
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,63 @@
+apply plugin: 'com.android.application'
+apply plugin: 'org.greenrobot.greendao'
+apply plugin: 'kotlin-android'
+
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.2"
+ defaultConfig {
+ applicationId "com.midnight.weatherforecast"
+ minSdkVersion 16
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ dataBinding {
+ enabled = true
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+
+}
+greendao {
+ schemaVersion 12
+ targetGenDir 'src/main/java'
+
+}
+
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.squareup.retrofit2:retrofit:2.6.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
+ implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.google.code.gson:gson:2.8.2'
+ implementation 'org.greenrobot:greendao:3.2.2'
+
+ implementation ("com.google.android.material:material:1.0.0")
+ implementation ("androidx.cardview:cardview:1.0.0")
+ implementation ("androidx.recyclerview:recyclerview:1.0.0")
+ implementation ("androidx.appcompat:appcompat:1.0.2")
+
+ testImplementation 'org.robolectric:robolectric:4.3'
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'androidx.test:core:1.2.0'
+ testImplementation "com.squareup.okhttp3:mockwebserver:3.6.0"
+
+ 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/com/midnight/weatherforecast/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/midnight/weatherforecast/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..affa957f
--- /dev/null
+++ b/app/src/androidTest/java/com/midnight/weatherforecast/ExampleInstrumentedTest.java
@@ -0,0 +1,27 @@
+package com.midnight.weatherforecast;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ assertEquals("com.midnight.weatherforecast", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..983bd850
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/controller/ControllerAPI.kt b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerAPI.kt
new file mode 100644
index 00000000..cc34545c
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerAPI.kt
@@ -0,0 +1,127 @@
+package com.midnight.weatherforecast.controller
+
+import com.midnight.weatherforecast.intersepts.GlobalIntersepts
+import com.midnight.weatherforecast.interfaces.INFApi
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByGeo
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByName
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater
+import com.midnight.weatherforecast.models.modelsResponse.ModelForecast
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import retrofit2.converter.scalars.ScalarsConverterFactory
+import java.util.concurrent.TimeUnit
+
+/**
+ *
+ */
+class ControllerAPI {
+ val API_KEY="62fc4256-8f8c-11e5-8994-feff819cdc9f"
+ val API_ADDRESS="http://worksample-api.herokuapp.com"
+
+ private var okHttpClient: OkHttpClient? = null
+ private var retrofitRegister: Retrofit? = null
+ private var serviceRegister: INFApi? = null
+
+ private var callWeatherByName: Call? = null
+ private var callWeatherByGeo: Call? = null
+
+ private var callWeatherByNameSync: Call? = null
+ private var callWeatherByGeoSync: Call? = null
+
+ private var callForecastByName: Call? = null
+
+
+ private object Holder { val INSTANCE = ControllerAPI()}
+ companion object {
+ val instance: ControllerAPI by lazy { ControllerAPI.Holder.INSTANCE}
+ }
+
+ /**
+ *
+ */
+ init {
+ initOkHttp()
+ initBuilder()
+ initService()
+ }
+
+ /**
+ *
+ */
+ private fun initOkHttp() {
+ val interceptor = HttpLoggingInterceptor()
+ interceptor.level = HttpLoggingInterceptor.Level.BODY
+ okHttpClient = OkHttpClient.Builder()
+ .connectTimeout(120, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .addInterceptor(GlobalIntersepts())
+ .addInterceptor(interceptor)
+ .build()
+ }
+
+ /**
+ *
+ */
+ private fun initBuilder() {
+ retrofitRegister = Retrofit.Builder()
+ .addConverterFactory(ScalarsConverterFactory.create())
+ .addConverterFactory(GsonConverterFactory.create())
+ .client(okHttpClient!!)
+ .baseUrl(API_ADDRESS)
+ .build()
+ }
+
+ /**
+ *
+ */
+ private fun initService() {
+ serviceRegister = retrofitRegister!!.create(INFApi::class.java)
+ }
+
+ /**
+ *
+ */
+ fun getCurrentWeatherByName(paramCurrentWeatherByName: ModelParamCurrentWeatherByName, callback: Callback) {
+ callWeatherByName = serviceRegister!!.currentWeaterByCityName(paramCurrentWeatherByName.cityName,paramCurrentWeatherByName.key)
+ callWeatherByName!!.enqueue(callback)
+ }
+
+ /**
+ *
+ */
+ fun getCurrentWeatherByGPS(paramCurrentWeatherByGeo: ModelParamCurrentWeatherByGeo, callback: Callback) {
+ callWeatherByGeo = serviceRegister!!.currentWeaterByCityGeo(paramCurrentWeatherByGeo.lat,paramCurrentWeatherByGeo.lon,paramCurrentWeatherByGeo.key)
+ callWeatherByGeo!!.enqueue(callback)
+ }
+
+ /**
+ *
+ */
+ fun getCurrentWeatherByName(paramCurrentWeatherByName: ModelParamCurrentWeatherByName): ModelCurrentWeater? {
+ callWeatherByGeoSync = serviceRegister!!.currentWeaterByCityName(paramCurrentWeatherByName.cityName,paramCurrentWeatherByName.key)
+ return callWeatherByGeoSync?.execute()?.body()
+ }
+
+ /**
+ *
+ */
+ fun getCurrentWeatherByGPS(paramCurrentWeatherByGeo: ModelParamCurrentWeatherByGeo): ModelCurrentWeater? {
+ callWeatherByNameSync = serviceRegister!!.currentWeaterByCityGeo(paramCurrentWeatherByGeo.lat,paramCurrentWeatherByGeo.lon,paramCurrentWeatherByGeo.key)
+ return callWeatherByGeoSync?.execute()?.body()
+ }
+
+ /**
+ *
+ */
+ fun getForecastWeatherByName(paramCurrentWeatherByName: ModelParamCurrentWeatherByName, callback: Callback) {
+ callForecastByName = serviceRegister!!.forecastWeaterByCityName(paramCurrentWeatherByName.cityName,paramCurrentWeatherByName.key)
+ callForecastByName!!.enqueue(callback)
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/controller/ControllerDB.kt b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerDB.kt
new file mode 100644
index 00000000..579d8ac2
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerDB.kt
@@ -0,0 +1,53 @@
+package com.midnight.weatherforecast.controller
+
+import android.database.sqlite.SQLiteDatabase
+import com.midnight.weatherforecast.core.Loader
+import com.midnight.weatherforecast.models.modelsDb.DaoMaster
+import com.midnight.weatherforecast.models.modelsDb.DaoSession
+import com.midnight.weatherforecast.utils.AppConstant
+import org.greenrobot.greendao.query.QueryBuilder
+
+/**
+ *
+ */
+class ControllerDB {
+ private var daoSession: DaoSession? = null
+ private var helper: DaoMaster.DevOpenHelper? = null
+ private var db: SQLiteDatabase? = null
+ private var daoMaster: DaoMaster? = null
+
+ private object Holder { val INSTANCE=ControllerDB()}
+ companion object {
+ val instance: ControllerDB by lazy { Holder.INSTANCE }
+ }
+
+ init {
+ helper = DaoMaster.DevOpenHelper(Loader.getInstanse().applicationContext, AppConstant.DB_NAME, null)
+ db = helper!!.writableDatabase
+ daoMaster = DaoMaster(db)
+ daoSession = daoMaster!!.newSession()
+ QueryBuilder.LOG_SQL = true
+ QueryBuilder.LOG_VALUES = true
+ }
+
+ /**
+ *
+ */
+ fun getDaoSession(): DaoSession? {
+ return daoSession
+ }
+
+ /**
+ *
+ */
+ fun getMaster(): DaoMaster{
+ return this.daoMaster!!
+ }
+
+ /**
+ *
+ */
+ fun getDb():SQLiteDatabase{
+ return db!!
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/controller/ControllerData.kt b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerData.kt
new file mode 100644
index 00000000..d31b6f85
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerData.kt
@@ -0,0 +1,265 @@
+package com.midnight.weatherforecast.controller
+
+import android.util.Log
+import com.midnight.weatherforecast.interfaces.INFForecast
+import com.midnight.weatherforecast.interfaces.INFLoadData
+import com.midnight.weatherforecast.interfaces.INFSearchCity
+import com.midnight.weatherforecast.models.modelsDb.City
+import com.midnight.weatherforecast.models.modelsDb.CityDao
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByGeo
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByName
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater
+import com.midnight.weatherforecast.models.modelsResponse.ModelForecast
+import com.midnight.weatherforecast.utils.DispatchQueue
+import com.midnight.weatherforecast.utils.TaskUnit
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
+
+/**
+ *
+ */
+class ControllerData {
+ var citiesWeater= arrayListOf()
+ private object Holder { val INSTANCE=ControllerData()}
+ companion object {
+ val instances : ControllerData by lazy { Holder.INSTANCE }
+ }
+
+ fun getHandler(): DispatchQueue? {
+ return TaskUnit.controllerDataThread
+ }
+
+ /**
+ *
+ */
+ fun saveCity(model:ModelCurrentWeater,owner:Boolean):Boolean{
+ var modelDaos=City()
+ modelDaos.cityName=model.name
+ modelDaos.cityId=model.id
+ if (owner)
+ modelDaos.owner=1
+ else
+ modelDaos.owner=2
+ ControllerDB.instance.getDaoSession()?.insertOrReplace(modelDaos)
+ return true
+ }
+
+ /**
+ *
+ */
+ fun addToCitiesList(model:ModelCurrentWeater){
+ var found=false
+ for (i in 0 until citiesWeater.size){
+ if (citiesWeater[i].id==model.id){
+ citiesWeater[i]=model
+ found=true
+ break
+ }
+ }
+ if (!found){
+ citiesWeater.add(model)
+ }
+ }
+
+ /**
+ *
+ */
+ fun removeACity(model:ModelCurrentWeater) {
+ val queryCity = ControllerDB.instance.getDaoSession()?.queryBuilder(City::class.java)?.where(CityDao.Properties.CityId.eq(model.id))?.build()?.unique()
+ if (queryCity != null ) {
+ ControllerDB.instance.getDaoSession()?.delete(queryCity)
+ }
+
+ var index=0
+ for (i in 0 until citiesWeater.size){
+ if (citiesWeater[i].id==model.id){
+ citiesWeater.removeAt(index)
+ break
+ }
+ index++
+ }
+ }
+
+ /**
+ *
+ */
+ fun fetchCity(model:ModelParamCurrentWeatherByName, delegate: INFLoadData, cache:Boolean, async:Boolean, isMainCity:Boolean) {
+ if (async){
+ ControllerAPI.instance.getCurrentWeatherByName(model, object : Callback {
+ override fun onResponse(call: Call, response: Response) {
+ addCityToList(response.body()!!,isMainCity,cache)
+ delegate.onSuccess()
+ }
+
+ override fun onFailure(call: Call, t: Throwable) {
+ Log.e("onFailure", "onFailure")
+ delegate.onFail()
+ }
+ })
+ }else{
+ getHandler()?.post {
+ val result=ControllerAPI.instance.getCurrentWeatherByName(model)
+ addCityToList(result!!,isMainCity,cache)
+ delegate.onSuccess()
+
+ }
+ }
+
+ }
+
+ /**
+ *
+ */
+ fun fetchCity(model:ModelParamCurrentWeatherByGeo, delegate: INFLoadData, cache:Boolean, async:Boolean, isMainCity:Boolean) {
+ if (async){
+ ControllerAPI.instance.getCurrentWeatherByGPS(model, object : Callback {
+ override fun onResponse(call: Call, response: Response) {
+ addCityToList(response.body()!!,isMainCity,cache)
+ delegate.onSuccess()
+ }
+
+ override fun onFailure(call: Call, t: Throwable) {
+ Log.e("onFailure", "onFailure")
+ delegate.onFail()
+ }
+ })
+ }else{
+ getHandler()?.post {
+ val result=ControllerAPI.instance.getCurrentWeatherByGPS(model)
+ addCityToList(result!!,isMainCity,cache)
+ delegate.onSuccess()
+ }
+ }
+
+ }
+
+ /**
+ *
+ */
+ fun loadData(delegate: INFLoadData){
+ var modelLocalCity=getCachedLocalCity()
+ if (modelLocalCity!=null)
+ fetchCity(ModelParamCurrentWeatherByName(modelLocalCity!!.cityName,ControllerAPI.instance.API_KEY),delegate,false,false,true)
+
+ var list=getCachedCity()
+ for (item in list!!){
+ fetchCity(ModelParamCurrentWeatherByName(item.cityName,ControllerAPI.instance.API_KEY),delegate,false,false,false)
+ }
+
+ }
+
+ private fun addCityToList(model:ModelCurrentWeater,isMainCity:Boolean,isCache:Boolean){
+ if (isMainCity){
+ val queryList = ControllerDB.instance.getDaoSession()?.queryBuilder(City::class.java)?.where(CityDao.Properties.Owner.eq(1))?.build()?.list()
+ var mainId=-1
+ if (queryList==null || queryList.size==0){
+ if (isCache)
+ saveCity(model,isMainCity)
+ }else{
+ val queryCity=queryList[0]
+ mainId= queryCity?.cityId!!
+ queryCity?.cityName=model.name
+ queryCity?.cityId=model.id
+ queryCity?.owner= 1
+ ControllerDB.instance.getDaoSession()?.update(queryCity)
+ }
+
+ var found=false
+ for (i in 0 until citiesWeater.size){
+ if (citiesWeater[i].id==mainId){
+ citiesWeater[i]=model
+ found=true
+ break
+ }
+ }
+ if (!found){
+ citiesWeater.add(model)
+ }
+
+ }else{
+ val queryCity = ControllerDB.instance.getDaoSession()?.queryBuilder(City::class.java)?.where(CityDao.Properties.CityId.eq(model.id))?.build()?.list()?.get(0)
+ if (queryCity==null && isCache){
+ saveCity(model,isMainCity)
+ }else{
+ queryCity?.cityName=model.name
+ queryCity?.cityId=model.id
+ queryCity?.owner= 2
+ ControllerDB.instance.getDaoSession()?.update(queryCity)
+ }
+
+ var found=false
+ for (i in 0 until citiesWeater.size){
+ if (citiesWeater[i].id==model.id){
+ citiesWeater[i]=model
+ found=true
+ break
+ }
+ }
+ if (!found){
+ citiesWeater.add(model)
+ }
+ }
+
+ }
+
+ /**
+ *
+ */
+ fun searchCity(model:ModelParamCurrentWeatherByName,delegate: INFSearchCity) {
+ ControllerAPI.instance.getCurrentWeatherByName(model, object : Callback {
+ override fun onResponse(call: Call, response: Response) {
+ if (response.body()!!.id!=null)
+ delegate.onSuccess(response.body()!!)
+ else
+ delegate.onFail()
+ }
+
+ override fun onFailure(call: Call, t: Throwable) {
+ Log.e("onFailure", "onFailure")
+ delegate.onFail()
+ }
+ })
+
+ }
+
+ /**
+ *
+ */
+ fun fetchForecast(model:ModelParamCurrentWeatherByName,delegate: INFForecast) {
+ ControllerAPI.instance.getForecastWeatherByName(model, object : Callback {
+ override fun onResponse(call: Call, response: Response) {
+ if (response.body()?.list!!.size>0)
+ delegate.onSuccess(response.body()!!)
+ else
+ delegate.onFail()
+ }
+
+ override fun onFailure(call: Call, t: Throwable) {
+ Log.e("onFailure", "onFailure")
+ delegate.onFail()
+ }
+ })
+ }
+
+ /**
+ *
+ */
+ fun getAllCachedCity(): MutableList? {
+ return ControllerDB.instance.getDaoSession()?.queryBuilder(City::class.java)?.build()?.list()
+ }
+
+ /**
+ *
+ */
+ fun getCachedCity(): MutableList? {
+ return ControllerDB.instance.getDaoSession()?.queryBuilder(City::class.java)?.where(CityDao.Properties.Owner.eq(2))?.build()?.list()
+ }
+
+ /**
+ *
+ */
+ fun getCachedLocalCity(): City? {
+ return ControllerDB.instance.getDaoSession()?.queryBuilder(City::class.java)?.where(CityDao.Properties.Owner.eq(1))?.build()?.unique()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/controller/ControllerLocation.java b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerLocation.java
new file mode 100644
index 00000000..84fb03a1
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerLocation.java
@@ -0,0 +1,54 @@
+package com.midnight.weatherforecast.controller;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ControllerLocation implements LocationListener {
+ private LocationManager locationManager;
+ private int TIME_INTERVAL = 100;
+ private int DISTANCE_INTERVAL = 1;
+ private String TAG="ControllerLocation";
+ public LocationManager getLocationManager() {
+ return locationManager;
+ }
+ private LocationListener delegate;
+
+ public ControllerLocation(Activity activity,LocationListener delegate) {
+ this.locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
+ this.delegate=delegate;
+ }
+
+ @SuppressLint("MissingPermission")
+ public void requestUpdates(){
+ this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, TIME_INTERVAL, DISTANCE_INTERVAL, this);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ Log.d(TAG, "Location found : " + location.getLatitude() + ", " + location.getLongitude() + (location.hasAccuracy() ? " : +- " + location.getAccuracy() + " meters" : ""));
+ this.delegate.onLocationChanged(location);
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ Log.d(TAG, "Provided status changed : " + provider + " : status : " + status);
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ Log.d(TAG, "Provider enabled : " + provider);
+ this.delegate.onProviderEnabled(provider);
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ Log.d(TAG, "Provider disabled : " + provider);
+ this.delegate.onProviderDisabled(provider);
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/controller/ControllerPermission.kt b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerPermission.kt
new file mode 100644
index 00000000..a73ad293
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/controller/ControllerPermission.kt
@@ -0,0 +1,64 @@
+package com.midnight.weatherforecast.controller
+
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.annotation.IntRange
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import com.midnight.weatherforecast.R
+import java.util.*
+
+class ControllerPermission {
+ val requestCodeLocation = 101
+ private val permissionLocation = Manifest.permission.ACCESS_FINE_LOCATION
+
+ private object Holder { val INSTANCE=ControllerPermission()}
+ companion object {
+ val instance : ControllerPermission by lazy { ControllerPermission.Holder.INSTANCE}
+ }
+
+ /**
+ *
+ */
+ fun getLocationPermission(context: Context,activity: Activity):Boolean{
+ return if (!checkPermission(context,permissionLocation)){
+ grantPermission(activity,requestCodeLocation,permissionLocation)
+ false
+ }else{
+ true
+ }
+ }
+
+ /**
+ *
+ */
+ private fun checkPermission(context: Context,permission: String): Boolean {
+ return if (Build.VERSION.SDK_INT >= 23) {
+ ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
+ } else {
+ true
+ }
+ }
+
+ /**
+ *
+ */
+ private fun grantPermission(activity: Activity, requestCode: Int, permission: String) {
+ executeGrantPermission(activity, permission, requestCode)
+ }
+
+ /**
+ *
+ */
+ private fun executeGrantPermission(activity: Activity, permissions: String, @IntRange(from = 0L) requestCode: Int) {
+ if (permissions.isNotEmpty()) {
+ val perm = arrayOfNulls(1)
+ perm[0]=permissions
+ ActivityCompat.requestPermissions(activity, perm, requestCode)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/core/Loader.java b/app/src/main/java/com/midnight/weatherforecast/core/Loader.java
new file mode 100644
index 00000000..c656de95
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/core/Loader.java
@@ -0,0 +1,58 @@
+package com.midnight.weatherforecast.core;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Handler;
+
+import androidx.annotation.NonNull;
+
+public class Loader extends Application {
+ public static volatile Handler applicationHandler;
+ public static Loader ourInstance;
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ ourInstance=this;
+ applicationHandler = new Handler(getApplicationContext().getMainLooper());
+ }
+
+ /**
+ * @return
+ */
+ public static Loader getInstanse() {
+ if (ourInstance == null)
+ ourInstance = new Loader();
+ return ourInstance;
+ }
+
+ /**
+ * @return
+ */
+ public Context getApplicationContext() {
+ return Loader.this;
+ }
+
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ }
+
+ @Override
+ public void onTerminate() {
+ super.onTerminate();
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/interfaces/INFApi.kt b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFApi.kt
new file mode 100644
index 00000000..15c84023
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFApi.kt
@@ -0,0 +1,28 @@
+package com.midnight.weatherforecast.interfaces
+
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater
+import com.midnight.weatherforecast.models.modelsResponse.ModelForecast
+import retrofit2.Call
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface INFApi {
+ @GET("weather")
+ abstract fun currentWeaterByCityName(@Query("q") cityName: String,@Query("key") token: String): Call
+
+ @GET("weather")
+ abstract fun currentWeaterByCityId(@Query("id") cityId: String,@Query("key") token: String): Call
+
+ @GET("weather")
+ abstract fun currentWeaterByCityGeo(@Query("lat") cityLat: String,@Query("lon") cityLon: String,@Query("key") token: String): Call
+
+ @GET("weather")
+ abstract fun currentWeaterByCityZip(@Query("zip") cityZip: String,@Query("key") token: String): Call
+
+ @GET("weather")
+ abstract fun currentWeaterByCitiesId(@Query("id") cityId: String,@Query("key") token: String): Call
+
+ @GET("forecast")
+ abstract fun forecastWeaterByCityName(@Query("q") cityName: String,@Query("key") token: String): Call
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/interfaces/INFForecast.kt b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFForecast.kt
new file mode 100644
index 00000000..cb17df46
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFForecast.kt
@@ -0,0 +1,9 @@
+package com.midnight.weatherforecast.interfaces
+
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater
+import com.midnight.weatherforecast.models.modelsResponse.ModelForecast
+
+interface INFForecast {
+ fun onSuccess(model:ModelForecast)
+ fun onFail()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/interfaces/INFListViewClick.java b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFListViewClick.java
new file mode 100644
index 00000000..ca8f6e78
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFListViewClick.java
@@ -0,0 +1,6 @@
+package com.midnight.weatherforecast.interfaces;
+
+public interface INFListViewClick {
+ public void onClickListenner(int position);
+ public void onLongClick(int position);
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/interfaces/INFLoadData.java b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFLoadData.java
new file mode 100644
index 00000000..08daef25
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFLoadData.java
@@ -0,0 +1,6 @@
+package com.midnight.weatherforecast.interfaces;
+
+public interface INFLoadData {
+ public void onSuccess();
+ public void onFail();
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/interfaces/INFSearchCity.kt b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFSearchCity.kt
new file mode 100644
index 00000000..a9454e82
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/interfaces/INFSearchCity.kt
@@ -0,0 +1,8 @@
+package com.midnight.weatherforecast.interfaces
+
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater
+
+interface INFSearchCity {
+ fun onSuccess(model:ModelCurrentWeater)
+ fun onFail()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/midnight/weatherforecast/intersepts/GlobalIntersepts.java b/app/src/main/java/com/midnight/weatherforecast/intersepts/GlobalIntersepts.java
new file mode 100644
index 00000000..f3ca653d
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/intersepts/GlobalIntersepts.java
@@ -0,0 +1,22 @@
+package com.midnight.weatherforecast.intersepts;
+
+import java.io.IOException;
+
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+
+public class GlobalIntersepts implements Interceptor {
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request request = chain.request();
+ okhttp3.Response response = chain.proceed(request);
+ if (response.code() == 403) {
+ //We have to show some error
+ return response;
+ }else {
+ return response;
+ }
+
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/City.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/City.java
new file mode 100644
index 00000000..b23b924e
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/City.java
@@ -0,0 +1,64 @@
+package com.midnight.weatherforecast.models.modelsDb;
+
+import org.greenrobot.greendao.annotation.Entity;
+import org.greenrobot.greendao.annotation.Id;
+import org.greenrobot.greendao.annotation.Index;
+import org.greenrobot.greendao.annotation.Property;
+import org.greenrobot.greendao.annotation.Generated;
+
+@Entity(nameInDb = "CITY_LIST")
+public class City {
+ @Id
+ private Long id;
+ @Property(nameInDb = "CITYID")
+ @Index(unique = true)
+ private int cityId;
+ @Property(nameInDb = "CITYNAME")
+ private String cityName;
+ @Property(nameInDb = "RECOWNER")
+ private int owner;
+
+ @Generated(hash = 1705308826)
+ public City(Long id, int cityId, String cityName, int owner) {
+ this.id = id;
+ this.cityId = cityId;
+ this.cityName = cityName;
+ this.owner = owner;
+ }
+
+ @Generated(hash = 750791287)
+ public City() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public int getCityId() {
+ return cityId;
+ }
+
+ public void setCityId(int cityId) {
+ this.cityId = cityId;
+ }
+
+ public String getCityName() {
+ return cityName;
+ }
+
+ public void setCityName(String cityName) {
+ this.cityName = cityName;
+ }
+
+ public int getOwner() {
+ return owner;
+ }
+
+ public void setOwner(int owner) {
+ this.owner = owner;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/CityDao.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/CityDao.java
new file mode 100644
index 00000000..97eed734
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/CityDao.java
@@ -0,0 +1,142 @@
+package com.midnight.weatherforecast.models.modelsDb;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteStatement;
+
+import org.greenrobot.greendao.AbstractDao;
+import org.greenrobot.greendao.Property;
+import org.greenrobot.greendao.internal.DaoConfig;
+import org.greenrobot.greendao.database.Database;
+import org.greenrobot.greendao.database.DatabaseStatement;
+
+// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
+/**
+ * DAO for table "CITY_LIST".
+*/
+public class CityDao extends AbstractDao {
+
+ public static final String TABLENAME = "CITY_LIST";
+
+ /**
+ * Properties of entity City.
+ * Can be used for QueryBuilder and for referencing column names.
+ */
+ public static class Properties {
+ public final static Property Id = new Property(0, Long.class, "id", true, "_id");
+ public final static Property CityId = new Property(1, int.class, "cityId", false, "CITYID");
+ public final static Property CityName = new Property(2, String.class, "cityName", false, "CITYNAME");
+ public final static Property Owner = new Property(3, int.class, "owner", false, "RECOWNER");
+ }
+
+
+ public CityDao(DaoConfig config) {
+ super(config);
+ }
+
+ public CityDao(DaoConfig config, DaoSession daoSession) {
+ super(config, daoSession);
+ }
+
+ /** Creates the underlying database table. */
+ public static void createTable(Database db, boolean ifNotExists) {
+ String constraint = ifNotExists? "IF NOT EXISTS ": "";
+ db.execSQL("CREATE TABLE " + constraint + "\"CITY_LIST\" (" + //
+ "\"_id\" INTEGER PRIMARY KEY ," + // 0: id
+ "\"CITYID\" INTEGER NOT NULL ," + // 1: cityId
+ "\"CITYNAME\" TEXT," + // 2: cityName
+ "\"RECOWNER\" INTEGER NOT NULL );"); // 3: owner
+ // Add Indexes
+ db.execSQL("CREATE UNIQUE INDEX " + constraint + "IDX_CITY_LIST_CITYID ON \"CITY_LIST\"" +
+ " (\"CITYID\" ASC);");
+ }
+
+ /** Drops the underlying database table. */
+ public static void dropTable(Database db, boolean ifExists) {
+ String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"CITY_LIST\"";
+ db.execSQL(sql);
+ }
+
+ @Override
+ protected final void bindValues(DatabaseStatement stmt, City entity) {
+ stmt.clearBindings();
+
+ Long id = entity.getId();
+ if (id != null) {
+ stmt.bindLong(1, id);
+ }
+ stmt.bindLong(2, entity.getCityId());
+
+ String cityName = entity.getCityName();
+ if (cityName != null) {
+ stmt.bindString(3, cityName);
+ }
+ stmt.bindLong(4, entity.getOwner());
+ }
+
+ @Override
+ protected final void bindValues(SQLiteStatement stmt, City entity) {
+ stmt.clearBindings();
+
+ Long id = entity.getId();
+ if (id != null) {
+ stmt.bindLong(1, id);
+ }
+ stmt.bindLong(2, entity.getCityId());
+
+ String cityName = entity.getCityName();
+ if (cityName != null) {
+ stmt.bindString(3, cityName);
+ }
+ stmt.bindLong(4, entity.getOwner());
+ }
+
+ @Override
+ public Long readKey(Cursor cursor, int offset) {
+ return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
+ }
+
+ @Override
+ public City readEntity(Cursor cursor, int offset) {
+ City entity = new City( //
+ cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
+ cursor.getInt(offset + 1), // cityId
+ cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // cityName
+ cursor.getInt(offset + 3) // owner
+ );
+ return entity;
+ }
+
+ @Override
+ public void readEntity(Cursor cursor, City entity, int offset) {
+ entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
+ entity.setCityId(cursor.getInt(offset + 1));
+ entity.setCityName(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
+ entity.setOwner(cursor.getInt(offset + 3));
+ }
+
+ @Override
+ protected final Long updateKeyAfterInsert(City entity, long rowId) {
+ entity.setId(rowId);
+ return rowId;
+ }
+
+ @Override
+ public Long getKey(City entity) {
+ if(entity != null) {
+ return entity.getId();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean hasKey(City entity) {
+ return entity.getId() != null;
+ }
+
+ @Override
+ protected final boolean isEntityUpdateable() {
+ return true;
+ }
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/DaoMaster.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/DaoMaster.java
new file mode 100644
index 00000000..f56e9a24
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/DaoMaster.java
@@ -0,0 +1,96 @@
+package com.midnight.weatherforecast.models.modelsDb;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.util.Log;
+
+import org.greenrobot.greendao.AbstractDaoMaster;
+import org.greenrobot.greendao.database.StandardDatabase;
+import org.greenrobot.greendao.database.Database;
+import org.greenrobot.greendao.database.DatabaseOpenHelper;
+import org.greenrobot.greendao.identityscope.IdentityScopeType;
+
+
+// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
+/**
+ * Master of DAO (schema version 12): knows all DAOs.
+ */
+public class DaoMaster extends AbstractDaoMaster {
+ public static final int SCHEMA_VERSION = 12;
+
+ /** Creates underlying database table using DAOs. */
+ public static void createAllTables(Database db, boolean ifNotExists) {
+ CityDao.createTable(db, ifNotExists);
+ }
+
+ /** Drops underlying database table using DAOs. */
+ public static void dropAllTables(Database db, boolean ifExists) {
+ CityDao.dropTable(db, ifExists);
+ }
+
+ /**
+ * WARNING: Drops all table on Upgrade! Use only during development.
+ * Convenience method using a {@link DevOpenHelper}.
+ */
+ public static DaoSession newDevSession(Context context, String name) {
+ Database db = new DevOpenHelper(context, name).getWritableDb();
+ DaoMaster daoMaster = new DaoMaster(db);
+ return daoMaster.newSession();
+ }
+
+ public DaoMaster(SQLiteDatabase db) {
+ this(new StandardDatabase(db));
+ }
+
+ public DaoMaster(Database db) {
+ super(db, SCHEMA_VERSION);
+ registerDaoClass(CityDao.class);
+ }
+
+ public DaoSession newSession() {
+ return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
+ }
+
+ public DaoSession newSession(IdentityScopeType type) {
+ return new DaoSession(db, type, daoConfigMap);
+ }
+
+ /**
+ * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
+ */
+ public static abstract class OpenHelper extends DatabaseOpenHelper {
+ public OpenHelper(Context context, String name) {
+ super(context, name, SCHEMA_VERSION);
+ }
+
+ public OpenHelper(Context context, String name, CursorFactory factory) {
+ super(context, name, factory, SCHEMA_VERSION);
+ }
+
+ @Override
+ public void onCreate(Database db) {
+ Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
+ createAllTables(db, false);
+ }
+ }
+
+ /** WARNING: Drops all table on Upgrade! Use only during development. */
+ public static class DevOpenHelper extends OpenHelper {
+ public DevOpenHelper(Context context, String name) {
+ super(context, name);
+ }
+
+ public DevOpenHelper(Context context, String name, CursorFactory factory) {
+ super(context, name, factory);
+ }
+
+ @Override
+ public void onUpgrade(Database db, int oldVersion, int newVersion) {
+ Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
+ dropAllTables(db, true);
+ onCreate(db);
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/DaoSession.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/DaoSession.java
new file mode 100644
index 00000000..dd7d85f3
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsDb/DaoSession.java
@@ -0,0 +1,48 @@
+package com.midnight.weatherforecast.models.modelsDb;
+
+import java.util.Map;
+
+import org.greenrobot.greendao.AbstractDao;
+import org.greenrobot.greendao.AbstractDaoSession;
+import org.greenrobot.greendao.database.Database;
+import org.greenrobot.greendao.identityscope.IdentityScopeType;
+import org.greenrobot.greendao.internal.DaoConfig;
+
+import com.midnight.weatherforecast.models.modelsDb.City;
+
+import com.midnight.weatherforecast.models.modelsDb.CityDao;
+
+// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
+
+/**
+ * {@inheritDoc}
+ *
+ * @see org.greenrobot.greendao.AbstractDaoSession
+ */
+public class DaoSession extends AbstractDaoSession {
+
+ private final DaoConfig cityDaoConfig;
+
+ private final CityDao cityDao;
+
+ public DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig>
+ daoConfigMap) {
+ super(db);
+
+ cityDaoConfig = daoConfigMap.get(CityDao.class).clone();
+ cityDaoConfig.initIdentityScope(type);
+
+ cityDao = new CityDao(cityDaoConfig, this);
+
+ registerDao(City.class, cityDao);
+ }
+
+ public void clear() {
+ cityDaoConfig.clearIdentityScope();
+ }
+
+ public CityDao getCityDao() {
+ return cityDao;
+ }
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByCitiesId.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByCitiesId.java
new file mode 100644
index 00000000..a59df740
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByCitiesId.java
@@ -0,0 +1,40 @@
+package com.midnight.weatherforecast.models.modelsParam;
+
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public class ModelParamCurrentWeatherByCitiesId {
+ private ArrayList ids=new ArrayList<>();
+ private String key;
+
+ public ModelParamCurrentWeatherByCitiesId(String cityId, String key) {
+ this.ids.add(cityId);
+ this.key = key;
+ }
+
+ public String getCityId() {
+ String result="";
+ synchronized (ids) {
+ for (int index = 0; index < ids.size(); index++) {
+ result = result + ids.get(index);
+ if (index < ids.size() - 1)
+ result = result + ",";
+ }
+ }
+ return result;
+ }
+
+ public void setCityId(String cityId) {
+ this.ids.add(cityId);
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByGeo.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByGeo.java
new file mode 100644
index 00000000..bc71a80e
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByGeo.java
@@ -0,0 +1,40 @@
+package com.midnight.weatherforecast.models.modelsParam;
+
+/**
+ *
+ */
+public class ModelParamCurrentWeatherByGeo {
+ private String lat;
+ private String lon;
+ private String key;
+
+ public ModelParamCurrentWeatherByGeo(String lat, String lon, String key) {
+ this.lat = lat;
+ this.lon = lon;
+ this.key = key;
+ }
+
+ public String getLat() {
+ return lat;
+ }
+
+ public void setLat(String lat) {
+ this.lat = lat;
+ }
+
+ public String getLon() {
+ return lon;
+ }
+
+ public void setLon(String lon) {
+ this.lon = lon;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherById.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherById.java
new file mode 100644
index 00000000..ea461807
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherById.java
@@ -0,0 +1,30 @@
+package com.midnight.weatherforecast.models.modelsParam;
+
+/**
+ *
+ */
+public class ModelParamCurrentWeatherById {
+ private String cityId;
+ private String key;
+
+ public ModelParamCurrentWeatherById(String cityId, String key) {
+ this.cityId = cityId;
+ this.key = key;
+ }
+
+ public String getCityId() {
+ return cityId;
+ }
+
+ public void setCityId(String cityId) {
+ this.cityId = cityId;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByName.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByName.java
new file mode 100644
index 00000000..347a32d8
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByName.java
@@ -0,0 +1,30 @@
+package com.midnight.weatherforecast.models.modelsParam;
+
+/**
+ *
+ */
+public class ModelParamCurrentWeatherByName {
+ private String cityName;
+ private String key;
+
+ public ModelParamCurrentWeatherByName(String cityName, String key) {
+ this.cityName = cityName;
+ this.key = key;
+ }
+
+ public String getCityName() {
+ return cityName;
+ }
+
+ public void setCityName(String cityName) {
+ this.cityName = cityName;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByZip.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByZip.java
new file mode 100644
index 00000000..c944701d
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsParam/ModelParamCurrentWeatherByZip.java
@@ -0,0 +1,30 @@
+package com.midnight.weatherforecast.models.modelsParam;
+
+/**
+ *
+ */
+public class ModelParamCurrentWeatherByZip {
+ private String zipCode;
+ private String key;
+
+ public ModelParamCurrentWeatherByZip(String zipCode, String key) {
+ this.zipCode = zipCode;
+ this.key = key;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsResponse/ModelCurrentWeater.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsResponse/ModelCurrentWeater.java
new file mode 100644
index 00000000..b62b16d1
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsResponse/ModelCurrentWeater.java
@@ -0,0 +1,171 @@
+package com.midnight.weatherforecast.models.modelsResponse;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import com.midnight.weatherforecast.models.modelsSub.ModelCloud;
+import com.midnight.weatherforecast.models.modelsSub.ModelCoord;
+import com.midnight.weatherforecast.models.modelsSub.ModelMain;
+import com.midnight.weatherforecast.models.modelsSub.ModelRain;
+import com.midnight.weatherforecast.models.modelsSub.ModelSys;
+import com.midnight.weatherforecast.models.modelsSub.ModelWeather;
+import com.midnight.weatherforecast.models.modelsSub.ModelWind;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class ModelCurrentWeater implements Serializable {
+ @SerializedName("coord")
+ @Expose
+ private ModelCoord coord;
+ @SerializedName("weather")
+ @Expose
+ private List weather = null;
+ @SerializedName("base")
+ @Expose
+ private String base;
+ @SerializedName("main")
+ @Expose
+ private ModelMain main;
+ @SerializedName("visibility")
+ @Expose
+ private Integer visibility;
+ @SerializedName("wind")
+ @Expose
+ private ModelWind wind;
+ @SerializedName("rain")
+ @Expose
+ private ModelRain rain;
+ @SerializedName("clouds")
+ @Expose
+ private ModelCloud clouds;
+ @SerializedName("dt")
+ @Expose
+ private Integer dt;
+ @SerializedName("sys")
+ @Expose
+ private ModelSys sys;
+ @SerializedName("timezone")
+ @Expose
+ private Integer timezone;
+ @SerializedName("id")
+ @Expose
+ private Integer id;
+ @SerializedName("name")
+ @Expose
+ private String name;
+ @SerializedName("cod")
+ @Expose
+ private Integer cod;
+
+ public ModelCoord getCoord() {
+ return coord;
+ }
+
+ public void setCoord(ModelCoord coord) {
+ this.coord = coord;
+ }
+
+ public List getWeather() {
+ return weather;
+ }
+
+ public void setWeather(List weather) {
+ this.weather = weather;
+ }
+
+ public String getBase() {
+ return base;
+ }
+
+ public void setBase(String base) {
+ this.base = base;
+ }
+
+ public ModelMain getMain() {
+ return main;
+ }
+
+ public void setMain(ModelMain main) {
+ this.main = main;
+ }
+
+ public Integer getVisibility() {
+ return visibility;
+ }
+
+ public void setVisibility(Integer visibility) {
+ this.visibility = visibility;
+ }
+
+ public ModelWind getWind() {
+ return wind;
+ }
+
+ public void setWind(ModelWind wind) {
+ this.wind = wind;
+ }
+
+ public ModelRain getRain() {
+ return rain;
+ }
+
+ public void setRain(ModelRain rain) {
+ this.rain = rain;
+ }
+
+ public ModelCloud getClouds() {
+ return clouds;
+ }
+
+ public void setClouds(ModelCloud clouds) {
+ this.clouds = clouds;
+ }
+
+ public Integer getDt() {
+ return dt;
+ }
+
+ public void setDt(Integer dt) {
+ this.dt = dt;
+ }
+
+ public ModelSys getSys() {
+ return sys;
+ }
+
+ public void setSys(ModelSys sys) {
+ this.sys = sys;
+ }
+
+ public Integer getTimezone() {
+ return timezone;
+ }
+
+ public void setTimezone(Integer timezone) {
+ this.timezone = timezone;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getCod() {
+ return cod;
+ }
+
+ public void setCod(Integer cod) {
+ this.cod = cod;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsResponse/ModelForecast.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsResponse/ModelForecast.java
new file mode 100644
index 00000000..b11975fd
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsResponse/ModelForecast.java
@@ -0,0 +1,53 @@
+package com.midnight.weatherforecast.models.modelsResponse;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+public class ModelForecast {
+ @SerializedName("cod")
+ @Expose
+ private String cod;
+ @SerializedName("message")
+ @Expose
+ private Integer message;
+ @SerializedName("cnt")
+ @Expose
+ private Integer cnt;
+ @SerializedName("list")
+ @Expose
+ private java.util.List list = null;
+
+ public String getCod() {
+ return cod;
+ }
+
+ public void setCod(String cod) {
+ this.cod = cod;
+ }
+
+ public Integer getMessage() {
+ return message;
+ }
+
+ public void setMessage(Integer message) {
+ this.message = message;
+ }
+
+ public Integer getCnt() {
+ return cnt;
+ }
+
+ public void setCnt(Integer cnt) {
+ this.cnt = cnt;
+ }
+
+ public List getList() {
+ return list;
+ }
+
+ public void setList(List list) {
+ this.list = list;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelCloud.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelCloud.java
new file mode 100644
index 00000000..0eb54a23
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelCloud.java
@@ -0,0 +1,18 @@
+package com.midnight.weatherforecast.models.modelsSub;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class ModelCloud {
+ @SerializedName("all")
+ @Expose
+ private Integer all;
+
+ public Integer getAll() {
+ return all;
+ }
+
+ public void setAll(Integer all) {
+ this.all = all;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelCoord.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelCoord.java
new file mode 100644
index 00000000..819ad94e
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelCoord.java
@@ -0,0 +1,29 @@
+package com.midnight.weatherforecast.models.modelsSub;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class ModelCoord {
+ @SerializedName("lon")
+ @Expose
+ private Double lon;
+ @SerializedName("lat")
+ @Expose
+ private Double lat;
+
+ public Double getLon() {
+ return lon;
+ }
+
+ public void setLon(Double lon) {
+ this.lon = lon;
+ }
+
+ public Double getLat() {
+ return lat;
+ }
+
+ public void setLat(Double lat) {
+ this.lat = lat;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelMain.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelMain.java
new file mode 100644
index 00000000..35ec589e
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelMain.java
@@ -0,0 +1,62 @@
+package com.midnight.weatherforecast.models.modelsSub;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class ModelMain {
+ @SerializedName("temp")
+ @Expose
+ private Double temp;
+ @SerializedName("pressure")
+ @Expose
+ private Integer pressure;
+ @SerializedName("humidity")
+ @Expose
+ private Integer humidity;
+ @SerializedName("temp_min")
+ @Expose
+ private Double tempMin;
+ @SerializedName("temp_max")
+ @Expose
+ private Double tempMax;
+
+ public Double getTemp() {
+ return temp;
+ }
+
+ public void setTemp(Double temp) {
+ this.temp = temp;
+ }
+
+ public Integer getPressure() {
+ return pressure;
+ }
+
+ public void setPressure(Integer pressure) {
+ this.pressure = pressure;
+ }
+
+ public Integer getHumidity() {
+ return humidity;
+ }
+
+ public void setHumidity(Integer humidity) {
+ this.humidity = humidity;
+ }
+
+ public Double getTempMin() {
+ return tempMin;
+ }
+
+ public void setTempMin(Double tempMin) {
+ this.tempMin = tempMin;
+ }
+
+ public Double getTempMax() {
+ return tempMax;
+ }
+
+ public void setTempMax(Double tempMax) {
+ this.tempMax = tempMax;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelRain.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelRain.java
new file mode 100644
index 00000000..3bb25bdf
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelRain.java
@@ -0,0 +1,18 @@
+package com.midnight.weatherforecast.models.modelsSub;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class ModelRain {
+ @SerializedName("1h")
+ @Expose
+ private Double _1h;
+
+ public Double get_1h() {
+ return _1h;
+ }
+
+ public void set_1h(Double _1h) {
+ this._1h = _1h;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelSys.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelSys.java
new file mode 100644
index 00000000..65fc9bfa
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelSys.java
@@ -0,0 +1,62 @@
+package com.midnight.weatherforecast.models.modelsSub;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class ModelSys {
+ @SerializedName("type")
+ @Expose
+ private Integer type;
+ @SerializedName("id")
+ @Expose
+ private Integer id;
+ @SerializedName("country")
+ @Expose
+ private String country;
+ @SerializedName("sunrise")
+ @Expose
+ private Integer sunrise;
+ @SerializedName("sunset")
+ @Expose
+ private Integer sunset;
+
+ public Integer getType() {
+ return type;
+ }
+
+ public void setType(Integer type) {
+ this.type = type;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public Integer getSunrise() {
+ return sunrise;
+ }
+
+ public void setSunrise(Integer sunrise) {
+ this.sunrise = sunrise;
+ }
+
+ public Integer getSunset() {
+ return sunset;
+ }
+
+ public void setSunset(Integer sunset) {
+ this.sunset = sunset;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelWeather.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelWeather.java
new file mode 100644
index 00000000..b5ef2b25
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelWeather.java
@@ -0,0 +1,51 @@
+package com.midnight.weatherforecast.models.modelsSub;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class ModelWeather {
+ @SerializedName("id")
+ @Expose
+ private Integer id;
+ @SerializedName("main")
+ @Expose
+ private String main;
+ @SerializedName("description")
+ @Expose
+ private String description;
+ @SerializedName("icon")
+ @Expose
+ private String icon;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getMain() {
+ return main;
+ }
+
+ public void setMain(String main) {
+ this.main = main;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelWind.java b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelWind.java
new file mode 100644
index 00000000..caab2ab2
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/models/modelsSub/ModelWind.java
@@ -0,0 +1,29 @@
+package com.midnight.weatherforecast.models.modelsSub;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class ModelWind {
+ @SerializedName("speed")
+ @Expose
+ private Double speed;
+ @SerializedName("deg")
+ @Expose
+ private Double deg;
+
+ public Double getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(Double speed) {
+ this.speed = speed;
+ }
+
+ public Double getDeg() {
+ return deg;
+ }
+
+ public void setDeg(Double deg) {
+ this.deg = deg;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/utils/AppConstant.java b/app/src/main/java/com/midnight/weatherforecast/utils/AppConstant.java
new file mode 100644
index 00000000..517ed3ce
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/utils/AppConstant.java
@@ -0,0 +1,118 @@
+package com.midnight.weatherforecast.utils;
+
+import com.midnight.weatherforecast.R;
+
+import java.util.HashMap;
+
+public class AppConstant {
+ public static final String Clouds="Clouds";
+ public static final String Clear="Clear";
+ public static final String Tornado="Tornado";
+ public static final String Squall="Squall";
+ public static final String Ash="Ash";
+ public static final String Sand="Sand";
+ public static final String Fog="Fog";
+ public static final String Dust="Dust";
+ public static final String Haze="Haze";
+ public static final String Smoke="Smoke";
+ public static final String Mist="Mist";
+ public static final String Snow="Snow";
+ public static final String Rain="Rain";
+ public static final String Drizzle="Drizzle";
+ public static final String Thunderstorm="Thunderstorm";
+
+ public static final HashMap iconList;
+ static {
+ iconList=new HashMap<>();
+ iconList.put(Clouds, R.drawable.ic_wi_cloud);
+ iconList.put(Clear, R.drawable.ic_wi_day_sunny);
+ iconList.put(Tornado, R.drawable.ic_wi_lightning);
+ iconList.put(Squall, R.drawable.ic_wi_day_light_wind);
+ iconList.put(Ash, R.drawable.ic_wi_day_light_wind);
+ iconList.put(Sand, R.drawable.ic_wi_day_light_wind);
+ iconList.put(Fog, R.drawable.ic_wi_cloudy_windy);
+ iconList.put(Dust, R.drawable.ic_wi_cloudy_windy);
+ iconList.put(Haze, R.drawable.ic_wi_cloudy_windy);
+ iconList.put(Smoke, R.drawable.ic_wi_day_light_wind);
+ iconList.put(Mist, R.drawable.ic_wi_cloudy_windy);
+ iconList.put(Snow, R.drawable.ic_wi_day_snow_wind);
+ iconList.put(Rain, R.drawable.ic_wi_day_rain);
+ iconList.put(Drizzle, R.drawable.ic_wi_day_rain);
+ iconList.put(Thunderstorm, R.drawable.ic_wi_lightning);
+ }
+
+ public static final HashMap iconListBlack;
+ static {
+ iconListBlack=new HashMap<>();
+ iconListBlack.put(Clouds, R.drawable.ic_wi_cloud_black);
+ iconListBlack.put(Clear, R.drawable.ic_wi_day_sunny_black);
+ iconListBlack.put(Tornado, R.drawable.ic_wi_lightning_black);
+ iconListBlack.put(Squall, R.drawable.ic_wi_day_light_wind_black);
+ iconListBlack.put(Ash, R.drawable.ic_wi_day_light_wind_black);
+ iconListBlack.put(Sand, R.drawable.ic_wi_day_light_wind_black);
+ iconListBlack.put(Fog, R.drawable.ic_wi_cloudy_windy_black);
+ iconListBlack.put(Dust, R.drawable.ic_wi_cloudy_windy_black);
+ iconListBlack.put(Haze, R.drawable.ic_wi_cloudy_windy_black);
+ iconListBlack.put(Smoke, R.drawable.ic_wi_day_light_wind_black);
+ iconListBlack.put(Mist, R.drawable.ic_wi_cloudy_windy_black);
+ iconListBlack.put(Snow, R.drawable.ic_wi_day_snow_wind_black);
+ iconListBlack.put(Rain, R.drawable.ic_wi_day_rain_black);
+ iconListBlack.put(Drizzle, R.drawable.ic_wi_day_rain_black);
+ iconListBlack.put(Thunderstorm, R.drawable.ic_wi_lightning_black);
+ }
+
+ public static final HashMap iconListMight;
+ static {
+ iconListMight=new HashMap<>();
+ iconListMight.put(Clouds, R.drawable.ic_wi_night_cloudy);
+ iconListMight.put(Clear, R.drawable.ic_wi_night_clear);
+ iconListMight.put(Tornado, R.drawable.ic_wi_lightning);
+ iconListMight.put(Squall, R.drawable.ic_wi_night_alt_cloudy_windy);
+ iconListMight.put(Ash, R.drawable.ic_wi_night_alt_cloudy_windy);
+ iconListMight.put(Sand, R.drawable.ic_wi_night_alt_cloudy_windy);
+ iconListMight.put(Fog, R.drawable.ic_wi_cloudy_windy);
+ iconListMight.put(Dust, R.drawable.ic_wi_cloudy_windy);
+ iconListMight.put(Haze, R.drawable.ic_wi_cloudy_windy);
+ iconListMight.put(Smoke, R.drawable.ic_wi_night_alt_cloudy_windy);
+ iconListMight.put(Mist, R.drawable.ic_wi_night_alt_cloudy_windy);
+ iconListMight.put(Snow, R.drawable.ic_wi_night_alt_snow);
+ iconListMight.put(Rain, R.drawable.ic_wi_night_alt_rain);
+ iconListMight.put(Drizzle, R.drawable.ic_wi_night_cloudy);
+ iconListMight.put(Thunderstorm, R.drawable.ic_wi_lightning);
+ }
+
+
+ public static final HashMap colorList;
+ static {
+ colorList=new HashMap<>();
+ colorList.put(Clouds, 0xFF6D5DF1);
+ colorList.put(Clear, 0xFFFFC107);
+ colorList.put(Tornado, 0xFF757575);
+ colorList.put(Squall, 0xFF757575);
+ colorList.put(Ash, 0xFF9E9E9E);
+ colorList.put(Sand, 0xFF795548);
+ colorList.put(Fog,0xFFF5F5F5);
+ colorList.put(Dust, 0xFF455A64);
+ colorList.put(Haze, 0xFFBDBDBD);
+ colorList.put(Smoke, 0xFFBDBDBD);
+ colorList.put(Mist, 0xFFBDBDBD);
+ colorList.put(Snow, 0xFFFFFFFF);
+ colorList.put(Rain, 0xFF0097A7);
+ colorList.put(Drizzle, 0xFF00BCD4);
+ colorList.put(Thunderstorm, 0xFF303F9F);
+ }
+
+ public static final HashMap dayOfWeek;
+ static {
+ dayOfWeek=new HashMap<>();
+ dayOfWeek.put(1, "Saturday");
+ dayOfWeek.put(2, "Sunday");
+ dayOfWeek.put(3, "Monday");
+ dayOfWeek.put(4, "Tuesday");
+ dayOfWeek.put(5, "Wednesday");
+ dayOfWeek.put(6, "Thursday");
+ dayOfWeek.put(7, "Friday");
+ }
+
+ public static String DB_NAME="weatherDB.db";
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/utils/DispatchQueue.java b/app/src/main/java/com/midnight/weatherforecast/utils/DispatchQueue.java
new file mode 100644
index 00000000..6b499c99
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/utils/DispatchQueue.java
@@ -0,0 +1,96 @@
+package com.midnight.weatherforecast.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.util.concurrent.CountDownLatch;
+
+public class DispatchQueue extends Thread {
+
+ private volatile Handler handler = null;
+ private CountDownLatch syncLatch = new CountDownLatch(1);
+
+ public DispatchQueue(final String threadName) {
+ setName(threadName);
+ start();
+ }
+
+ public void sendMessage(Message msg, int delay) {
+ try {
+ syncLatch.await();
+ if (delay <= 0) {
+ handler.sendMessage(msg);
+ } else {
+ handler.sendMessageDelayed(msg, delay);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void cancelRunnable(Runnable runnable) {
+ try {
+ syncLatch.await();
+ handler.removeCallbacks(runnable);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void post(Runnable runnable) {
+ postRunnable(runnable, 0);
+ }
+
+
+ public void postRunnable(Runnable runnable) {
+ postRunnable(runnable, 0);
+ }
+
+ public void postRunnable(Runnable runnable, long delay) {
+ try {
+ syncLatch.await();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (delay <= 0) {
+ handler.post(runnable);
+ } else {
+ handler.postDelayed(runnable, delay);
+ }
+ }
+
+ public void cleanupQueue() {
+ try {
+ syncLatch.await();
+ handler.removeCallbacksAndMessages(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void handleMessage(Message inputMessage) {
+
+ }
+
+ public void recycle() {
+ handler.getLooper().quit();
+ }
+
+ @Override
+ public void run() {
+ Looper.prepare();
+ handler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ DispatchQueue.this.handleMessage(msg);
+ }
+ };
+ syncLatch.countDown();
+ Looper.loop();
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/utils/TaskUnit.java b/app/src/main/java/com/midnight/weatherforecast/utils/TaskUnit.java
new file mode 100644
index 00000000..18950a10
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/utils/TaskUnit.java
@@ -0,0 +1,6 @@
+package com.midnight.weatherforecast.utils;
+
+public class TaskUnit {
+ public static volatile DispatchQueue controllerDataThread = new DispatchQueue("controllerData");
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/utils/Utils.java b/app/src/main/java/com/midnight/weatherforecast/utils/Utils.java
new file mode 100644
index 00000000..6c8af8c0
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/utils/Utils.java
@@ -0,0 +1,45 @@
+package com.midnight.weatherforecast.utils;
+
+import android.text.format.DateFormat;
+import android.util.Log;
+
+import com.midnight.weatherforecast.core.Loader;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class Utils {
+ /**
+ *
+ * @param runnable
+ */
+ public static void runOnUiThread(Runnable runnable){
+ Loader.applicationHandler.post(runnable);
+ }
+
+ public static int getHourOfDay(int date){
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+ calendar.setTimeInMillis(Long.valueOf(date)*1000);
+ return calendar.get(Calendar.HOUR_OF_DAY);
+
+ }
+
+ public static String getDayOfWeek(int date){
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+ calendar.setTimeInMillis(Long.valueOf(date)*1000);
+ return AppConstant.dayOfWeek.get(calendar.get(Calendar.DAY_OF_WEEK));
+
+ }
+
+ public static String getDateFormat(int date){
+ Log.e("date","->" + date);
+ Calendar cal = Calendar.getInstance(Locale.ENGLISH);
+ cal.setTimeInMillis(Long.valueOf(date)*1000);
+ return DateFormat.format("HH:mm", cal).toString();
+
+ }
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/views/MainActivity.java b/app/src/main/java/com/midnight/weatherforecast/views/MainActivity.java
new file mode 100644
index 00000000..f1cfb56d
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/views/MainActivity.java
@@ -0,0 +1,93 @@
+package com.midnight.weatherforecast.views;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.view.MenuItem;
+
+import com.midnight.weatherforecast.R;
+import com.midnight.weatherforecast.controller.ControllerPermission;
+import com.midnight.weatherforecast.databinding.ActivityMainBinding;
+import com.midnight.weatherforecast.views.fragments.FragmentDetail;
+import com.midnight.weatherforecast.views.fragments.FragmentMain;
+import com.midnight.weatherforecast.views.fragments.FragmentSearch;
+
+public class MainActivity extends AppCompatActivity {
+ private ActivityMainBinding binding;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding= DataBindingUtil.setContentView(this, R.layout.activity_main);
+ initView();
+
+ }
+
+ /**
+ *
+ */
+ private void initView(){
+ getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame,FragmentMain.getInstance(),FragmentMain.TAG)
+ .addToBackStack(null)
+ .commit();
+ setSupportActionBar(binding.toolbar);
+ binding.toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
+ binding.toolbar.setNavigationIcon(R.drawable.ic_back);
+ if (getSupportActionBar() != null){
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ }
+ }
+
+ public void openAddCityFragment(){
+ getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame,FragmentSearch.getInstance(), FragmentSearch.TAG)
+ .addToBackStack(FragmentSearch.TAG)
+ .commit();
+
+ }
+
+ public void openDetailFragment(Bundle bundle){
+ getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, FragmentDetail.getInstance(bundle), FragmentSearch.TAG)
+ .addToBackStack(FragmentSearch.TAG)
+ .commit();
+
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (grantResults[0]==-1){
+ ((FragmentMain)getSupportFragmentManager().getFragments().get(0)).handlerLoading(false);
+ return;
+ }
+ if (ControllerPermission.Companion.getInstance().getRequestCodeLocation()==requestCode){
+ if (getSupportFragmentManager().getFragments().size()>0){
+ if (getSupportFragmentManager().getFragments().get(0) instanceof FragmentMain){
+ ((FragmentMain)getSupportFragmentManager().getFragments().get(0)).getLocation();
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ onBackPressed();
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (getSupportFragmentManager().getFragments().size()>0){
+ if (getSupportFragmentManager().getFragments().get(0) instanceof FragmentDetail){
+ getSupportFragmentManager().popBackStack();
+ }else if (getSupportFragmentManager().getFragments().get(0) instanceof FragmentSearch){
+ getSupportFragmentManager().popBackStack();
+ }else{
+ finishAffinity();
+ }
+ }
+ }
+
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterForecast.java b/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterForecast.java
new file mode 100644
index 00000000..ee0f800a
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterForecast.java
@@ -0,0 +1,161 @@
+package com.midnight.weatherforecast.views.adapters;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.databinding.DataBindingUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.midnight.weatherforecast.R;
+import com.midnight.weatherforecast.controller.ControllerData;
+import com.midnight.weatherforecast.databinding.AdapterDetailBinding;
+import com.midnight.weatherforecast.databinding.AdapterForecastBinding;
+import com.midnight.weatherforecast.databinding.AdapterMainBinding;
+import com.midnight.weatherforecast.interfaces.INFListViewClick;
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater;
+import com.midnight.weatherforecast.utils.AppConstant;
+import com.midnight.weatherforecast.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AdapterForecast extends RecyclerView.Adapter{
+ private Context context;
+ private INFListViewClick delegate;
+ private ArrayList data=new ArrayList<>();
+ public AdapterForecast(Context context, INFListViewClick delegate,ArrayList data) {
+ this.context = context;
+ this.delegate=delegate;
+ this.data=data;
+ }
+
+ public AdapterForecast(Context context, INFListViewClick delegate) {
+ this.context = context;
+ this.delegate=delegate;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position==0){
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ switch (viewType){
+ case 0:
+ return new cityWeatherDetailViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.adapter_detail, parent, false));
+ case 1:
+ return new cityWeatherForecastViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.adapter_forecast, parent, false));
+ default:
+ return new cityWeatherForecastViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.adapter_search, parent, false));
+ }
+ }
+
+
+ @Override
+ public int getItemCount() {
+ return data.size();
+ }
+
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
+ if (viewHolder instanceof cityWeatherDetailViewHolder){
+ ModelCurrentWeater model=data.get(position);
+ final cityWeatherDetailViewHolder holder = (cityWeatherDetailViewHolder) viewHolder;
+ holder.binding.tvCityName.setText(model.getName());
+ holder.binding.tvCityTemp.setText(String.valueOf(Math.round(model.getMain().getTemp())));
+ holder.binding.tvCityMinTemp.setText(String.format(context.getString(R.string.min_temp),String.valueOf(Math.round(model.getMain().getTempMin()))));
+ holder.binding.tvCityMaxTemp.setText(String.format(context.getString(R.string.max_temp),String.valueOf(Math.round(model.getMain().getTempMax()))));
+ holder.binding.tvCityPressure.setText(String.format(context.getString(R.string.pressure),String.valueOf(Math.round(model.getMain().getPressure()))));
+ holder.binding.tvCityStatus.setText(model.getWeather().get(0).getMain());
+ holder.binding.tvCityStatusDesc.setText(model.getWeather().get(0).getDescription());
+ holder.binding.tvCityHum.setText(String.format(context.getString(R.string.humidity),String.valueOf(model.getMain().getHumidity())));
+ holder.binding.tvCityWind.setText(String.format(context.getString(R.string.wind),String.valueOf(model.getWind().getSpeed())));
+ holder.binding.imgIcon.setImageResource(AppConstant.iconList.get(model.getWeather().get(0).getMain()));
+ holder.binding.cardview.setCardBackgroundColor(AppConstant.colorList.get(model.getWeather().get(0).getMain()));
+
+ if ("Snow".equalsIgnoreCase(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain()) || "Fog".equalsIgnoreCase(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain())){
+ holder.binding.tvCityName.setTextColor(0xFF000000);
+ holder.binding.tvCityTemp.setTextColor(0xFF000000);
+ holder.binding.tvCityStatus.setTextColor(0xFF000000);
+ holder.binding.tvCityStatusDesc.setTextColor(0xFF000000);
+ holder.binding.tvCityHum.setTextColor(0xFF000000);
+ holder.binding.tvCityWind.setTextColor(0xFF000000);
+ holder.binding.imgIcon.setImageResource(AppConstant.iconListBlack.get(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain()));
+ }
+
+
+ }else if (viewHolder instanceof cityWeatherForecastViewHolder){
+ ModelCurrentWeater model=data.get(position);
+ final cityWeatherForecastViewHolder holder = (cityWeatherForecastViewHolder) viewHolder;
+ holder.binding.tvDay.setText(Utils.getDayOfWeek(model.getDt()));
+ holder.binding.tvDate.setText(Utils.getDateFormat(model.getDt()));
+ holder.binding.tvCityTemp.setText(String.valueOf(Math.round(model.getMain().getTemp())));
+ holder.binding.tvCityStatus.setText(model.getWeather().get(0).getMain());
+ holder.binding.imgIcon.setImageResource(AppConstant.iconList.get(model.getWeather().get(0).getMain()));
+ holder.binding.cardview.setCardBackgroundColor(AppConstant.colorList.get(model.getWeather().get(0).getMain()));
+
+ if ("Snow".equalsIgnoreCase(model.getWeather().get(0).getMain()) || "Fog".equalsIgnoreCase(model.getWeather().get(0).getMain())){
+ holder.binding.tvDay.setTextColor(0xFF000000);
+ holder.binding.tvDate.setTextColor(0xFF000000);
+ holder.binding.tvCityTemp.setTextColor(0xFF000000);
+ holder.binding.tvCityStatus.setTextColor(0xFF000000);
+ holder.binding.imgIcon.setImageResource(AppConstant.iconListBlack.get(model.getWeather().get(0).getMain()));
+ }
+ int hours= Utils.getHourOfDay(model.getDt());
+ if (hours>19 || hours<6){
+ holder.binding.cardview.setCardBackgroundColor(0xFF1A2636);
+ holder.binding.imgIcon.setImageResource(AppConstant.iconListMight.get(model.getWeather().get(0).getMain()));
+ }
+
+ }
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public ViewHolder(View v) {
+ super(v);
+ }
+ }
+
+ /**
+ *
+ */
+ public class cityWeatherForecastViewHolder extends ViewHolder{
+ private AdapterForecastBinding binding;
+
+ public cityWeatherForecastViewHolder(@NonNull AdapterForecastBinding itemView) {
+ super(itemView.getRoot());
+ binding=itemView;
+ }
+ }
+
+ public class cityWeatherDetailViewHolder extends ViewHolder{
+ private AdapterDetailBinding binding;
+
+ public cityWeatherDetailViewHolder(@NonNull AdapterDetailBinding itemView) {
+ super(itemView.getRoot());
+ binding=itemView;
+ }
+ }
+
+ public void addData(ModelCurrentWeater modelCurrentWeater,int position){
+ this.data.add(position,modelCurrentWeater);
+ notifyDataSetChanged();
+ }
+
+ public void addData(List data){
+ this.data.addAll(data);
+ notifyDataSetChanged();
+ }
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterMain.java b/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterMain.java
new file mode 100644
index 00000000..354efac2
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterMain.java
@@ -0,0 +1,80 @@
+package com.midnight.weatherforecast.views.adapters;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.databinding.DataBindingUtil;
+import androidx.recyclerview.widget.RecyclerView;
+import com.midnight.weatherforecast.R;
+import com.midnight.weatherforecast.controller.ControllerData;
+import com.midnight.weatherforecast.databinding.AdapterMainBinding;
+import com.midnight.weatherforecast.interfaces.INFListViewClick;
+import com.midnight.weatherforecast.utils.AppConstant;
+import com.midnight.weatherforecast.utils.Utils;
+
+public class AdapterMain extends RecyclerView.Adapter{
+ private Context context;
+ private INFListViewClick delegate;
+ public AdapterMain(Context context,INFListViewClick delegate) {
+ this.context = context;
+ this.delegate=delegate;
+ }
+
+
+ @NonNull
+ @Override
+ public cityWeatherViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new cityWeatherViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.adapter_main, parent, false));
+ }
+
+
+ @Override
+ public int getItemCount() {
+ return ControllerData.Companion.getInstances().getCitiesWeater().size();
+ }
+
+
+ @Override
+ public void onBindViewHolder(@NonNull cityWeatherViewHolder holder, int position) {
+ holder.binding.tvCityName.setText(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getName());
+ holder.binding.tvCityTemp.setText(String.valueOf(Math.round(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getMain().getTemp())));
+ holder.binding.tvCityStatus.setText(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain());
+ holder.binding.tvCityStatusDesc.setText(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getDescription());
+ holder.binding.tvCityHum.setText(String.format(context.getString(R.string.humidity),String.valueOf(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getMain().getHumidity())));
+ holder.binding.tvCityWind.setText(String.format(context.getString(R.string.wind),String.valueOf(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWind().getSpeed())));
+ holder.binding.imgIcon.setImageResource(AppConstant.iconList.get(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain()));
+ holder.binding.cardview.setCardBackgroundColor(AppConstant.colorList.get(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain()));
+
+ if ("Snow".equalsIgnoreCase(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain()) || "Fog".equalsIgnoreCase(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain())){
+ holder.binding.tvCityName.setTextColor(0xFF000000);
+ holder.binding.tvCityTemp.setTextColor(0xFF000000);
+ holder.binding.tvCityStatus.setTextColor(0xFF000000);
+ holder.binding.tvCityStatusDesc.setTextColor(0xFF000000);
+ holder.binding.tvCityHum.setTextColor(0xFF000000);
+ holder.binding.tvCityWind.setTextColor(0xFF000000);
+ holder.binding.imgIcon.setImageResource(AppConstant.iconListBlack.get(ControllerData.Companion.getInstances().getCitiesWeater().get(position).getWeather().get(0).getMain()));
+ }
+
+ holder.binding.cardview.setOnClickListener(v -> delegate.onClickListenner(position));
+
+ holder.binding.cardview.setOnLongClickListener(v -> {
+ delegate.onLongClick(position);
+ return true;
+ });
+ }
+
+ /**
+ *
+ */
+ public class cityWeatherViewHolder extends RecyclerView.ViewHolder{
+ private AdapterMainBinding binding;
+
+ public cityWeatherViewHolder(@NonNull AdapterMainBinding itemView) {
+ super(itemView.getRoot());
+ binding=itemView;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterSearch.java b/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterSearch.java
new file mode 100644
index 00000000..a83a6818
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/views/adapters/AdapterSearch.java
@@ -0,0 +1,84 @@
+package com.midnight.weatherforecast.views.adapters;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.databinding.DataBindingUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.midnight.weatherforecast.R;
+import com.midnight.weatherforecast.controller.ControllerData;
+import com.midnight.weatherforecast.databinding.AdapterSearchBinding;
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater;
+import com.midnight.weatherforecast.utils.AppConstant;
+import com.midnight.weatherforecast.utils.Utils;
+
+public class AdapterSearch extends RecyclerView.Adapter{
+ private Context context;
+ private ModelCurrentWeater model;
+
+ public AdapterSearch(Context context) {
+ this.context = context;
+ }
+
+ public AdapterSearch(Context context, ModelCurrentWeater model) {
+ this.context = context;
+ this.model = model;
+ }
+
+ @NonNull
+ @Override
+ public cityWeatherViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new cityWeatherViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.adapter_search, parent, false));
+ }
+
+
+ @Override
+ public int getItemCount() {
+ if (model==null)
+ return 0;
+ else
+ return 1;
+ }
+
+
+ @Override
+ public void onBindViewHolder(@NonNull cityWeatherViewHolder holder, int position) {
+ holder.binding.tvCityName.setText(model.getName());
+ holder.binding.tvCityTemp.setText(String.valueOf(Math.round(model.getMain().getTemp())));
+ holder.binding.tvCityStatus.setText(model.getWeather().get(0).getMain());
+ holder.binding.imgIcon.setImageResource(AppConstant.iconList.get(model.getWeather().get(0).getMain()));
+ holder.binding.cardview.setCardBackgroundColor(AppConstant.colorList.get(model.getWeather().get(0).getMain()));
+
+ if ("Snow".equalsIgnoreCase(model.getWeather().get(0).getMain()) || "Fog".equalsIgnoreCase(model.getWeather().get(0).getMain())){
+ holder.binding.tvCityName.setTextColor(0xFF000000);
+ holder.binding.tvCityTemp.setTextColor(0xFF000000);
+ holder.binding.tvCityStatus.setTextColor(0xFF000000);
+ holder.binding.imgIcon.setImageResource(AppConstant.iconListBlack.get(model.getWeather().get(0).getMain()));
+ }
+
+ }
+
+ /**
+ *
+ */
+ public class cityWeatherViewHolder extends RecyclerView.ViewHolder{
+ private AdapterSearchBinding binding;
+
+ public cityWeatherViewHolder(@NonNull AdapterSearchBinding itemView) {
+ super(itemView.getRoot());
+ binding=itemView;
+ }
+ }
+
+ public void update(ModelCurrentWeater mode){
+ this.model=mode;
+ notifyDataSetChanged();
+ }
+
+ public ModelCurrentWeater getModel() {
+ return model;
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentDetail.java b/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentDetail.java
new file mode 100644
index 00000000..f5a9612e
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentDetail.java
@@ -0,0 +1,106 @@
+package com.midnight.weatherforecast.views.fragments;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.databinding.DataBindingUtil;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.midnight.weatherforecast.R;
+import com.midnight.weatherforecast.controller.ControllerAPI;
+import com.midnight.weatherforecast.controller.ControllerData;
+import com.midnight.weatherforecast.databinding.FragmentDetailBinding;
+import com.midnight.weatherforecast.interfaces.INFForecast;
+import com.midnight.weatherforecast.interfaces.INFListViewClick;
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByName;
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater;
+import com.midnight.weatherforecast.models.modelsResponse.ModelForecast;
+import com.midnight.weatherforecast.utils.Utils;
+import com.midnight.weatherforecast.views.adapters.AdapterForecast;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FragmentDetail extends Fragment implements INFListViewClick {
+ public static String TAG=FragmentDetail.class.getName();
+ private FragmentDetailBinding binding;
+ private AdapterForecast adapter;
+ public static FragmentDetail getInstance(Bundle bundle){
+ FragmentDetail model=new FragmentDetail();
+ model.setArguments(bundle);
+ return model;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ initView();
+ initClick();
+ initData();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = DataBindingUtil.inflate(inflater, R.layout.fragment_detail, container, false);
+ return binding.getRoot();
+ }
+
+ private void initView(){
+
+ }
+
+ private void initClick(){
+
+ }
+
+ private void initData() {
+ Bundle bundle = this.getArguments();
+ if (bundle != null) {
+ binding.pbLoading.setVisibility(View.VISIBLE);
+ ModelCurrentWeater modelCurrentWeater= (ModelCurrentWeater) bundle.getSerializable("current");
+ adapter=new AdapterForecast(getContext(),this);
+ binding.rclMain.setLayoutManager(new LinearLayoutManager(getContext()));
+ binding.rclMain.setAdapter(adapter);
+ adapter.addData(modelCurrentWeater,0);
+ ControllerData.Companion.getInstances().fetchForecast(new ModelParamCurrentWeatherByName(modelCurrentWeater.getName(), ControllerAPI.Companion.getInstance().getAPI_KEY()), new INFForecast() {
+ @Override
+ public void onSuccess(@NotNull ModelForecast model) {
+ if (getView()!=null)
+ Utils.runOnUiThread(() -> {
+ binding.pbLoading.setVisibility(View.GONE);
+ adapter.addData(model.getList());
+ });
+ }
+
+ @Override
+ public void onFail() {
+ if (getView()!=null)
+ Utils.runOnUiThread(() -> {
+ binding.pbLoading.setVisibility(View.GONE);
+ });
+
+ }
+ });
+ }
+
+ }
+
+ @Override
+ public void onClickListenner(int position) {
+
+ }
+
+ @Override
+ public void onLongClick(int position) {
+
+ }
+}
+
diff --git a/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentMain.java b/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentMain.java
new file mode 100644
index 00000000..11d4f3ed
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentMain.java
@@ -0,0 +1,172 @@
+package com.midnight.weatherforecast.views.fragments;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.location.Location;
+import android.location.LocationListener;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.databinding.DataBindingUtil;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.midnight.weatherforecast.R;
+import com.midnight.weatherforecast.controller.ControllerAPI;
+import com.midnight.weatherforecast.controller.ControllerData;
+import com.midnight.weatherforecast.controller.ControllerLocation;
+import com.midnight.weatherforecast.controller.ControllerPermission;
+import com.midnight.weatherforecast.databinding.FragmentMainBinding;
+import com.midnight.weatherforecast.interfaces.INFListViewClick;
+import com.midnight.weatherforecast.interfaces.INFLoadData;
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByGeo;
+import com.midnight.weatherforecast.utils.Utils;
+import com.midnight.weatherforecast.views.MainActivity;
+import com.midnight.weatherforecast.views.adapters.AdapterMain;
+
+public class FragmentMain extends Fragment implements LocationListener, INFListViewClick {
+ public static String TAG=FragmentMain.class.getName();
+ private FragmentMainBinding binding;
+ private AdapterMain adapterMain;
+ private ControllerLocation locationUtils;
+ public static FragmentMain getInstance(){
+ return new FragmentMain();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ initView();
+ initClick();
+ getPermission();
+ initData();
+ }
+
+ private void initData(){
+ handlerLoading(true);
+ ControllerData.Companion.getInstances().loadData(new INFLoadData() {
+ @Override
+ public void onSuccess() {
+ if (getView()!=null)
+ Utils.runOnUiThread(() -> {
+ handlerLoading(false);
+ adapterMain.notifyDataSetChanged();
+ });
+ }
+
+ @Override
+ public void onFail() {
+
+ }
+ });
+ }
+
+ /**
+ *
+ */
+ private void initView(){
+ adapterMain=new AdapterMain(getContext(),this);
+ binding.rclMain.setLayoutManager(new LinearLayoutManager(getContext()));
+ binding.rclMain.setAdapter(adapterMain);
+ }
+
+ private void initClick(){
+ binding.fabAdd.setOnClickListener(v -> ((MainActivity)getActivity()).openAddCityFragment());
+ }
+
+
+
+ /**
+ *
+ */
+ private void getPermission(){
+ if (ControllerPermission.Companion.getInstance().getLocationPermission(getContext(),getActivity())){
+ getLocation();
+ }
+ }
+
+ /**
+ *
+ */
+ public void getLocation(){
+ locationUtils = new ControllerLocation(getActivity(),this);
+ locationUtils.requestUpdates();
+ }
+
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ ControllerData.Companion.getInstances().fetchCity(new ModelParamCurrentWeatherByGeo(String.valueOf(location.getLatitude()), String.valueOf(location.getLongitude()), ControllerAPI.Companion.getInstance().getAPI_KEY()), new INFLoadData() {
+ @Override
+ public void onSuccess() {
+ if (getView()!=null)
+ Utils.runOnUiThread(() -> adapterMain.notifyDataSetChanged());
+ }
+
+ @Override
+ public void onFail() {
+
+ }
+ },true,true,true);
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+
+ }
+
+ @Override
+ public void onClickListenner(int position) {
+ Bundle bnd=new Bundle();
+ bnd.putSerializable("current",ControllerData.Companion.getInstances().getCitiesWeater().get(position));
+ ((MainActivity)getActivity()).openDetailFragment(bnd);
+ }
+
+ @Override
+ public void onLongClick(int position) {
+ if (position==0)
+ return;
+ new AlertDialog.Builder(getContext())
+ .setTitle(getString(R.string.dialog_delete_title))
+ .setMessage(getString(R.string.dialog_delete_message))
+ .setPositiveButton(android.R.string.yes, (dialog, which) -> {
+ ControllerData.Companion.getInstances().removeACity(ControllerData.Companion.getInstances().getCitiesWeater().get(position));
+ adapterMain.notifyDataSetChanged();
+ })
+ .setNegativeButton(android.R.string.no, (dialog, which) -> {
+
+ })
+ .setNegativeButton(android.R.string.no, null)
+ .show();
+
+ }
+
+ public void handlerLoading(boolean isShow){
+ if (isShow){
+ binding.pbLoading.setVisibility(View.VISIBLE);
+ }else {
+ binding.pbLoading.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentSearch.java b/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentSearch.java
new file mode 100644
index 00000000..ccdfe16f
--- /dev/null
+++ b/app/src/main/java/com/midnight/weatherforecast/views/fragments/FragmentSearch.java
@@ -0,0 +1,125 @@
+package com.midnight.weatherforecast.views.fragments;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.databinding.DataBindingUtil;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.midnight.weatherforecast.R;
+import com.midnight.weatherforecast.controller.ControllerAPI;
+import com.midnight.weatherforecast.controller.ControllerData;
+import com.midnight.weatherforecast.databinding.FragmentAddCityBinding;
+import com.midnight.weatherforecast.interfaces.INFSearchCity;
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByName;
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater;
+import com.midnight.weatherforecast.utils.Utils;
+import com.midnight.weatherforecast.views.MainActivity;
+import com.midnight.weatherforecast.views.adapters.AdapterSearch;
+
+import org.jetbrains.annotations.NotNull;
+
+import okhttp3.internal.Util;
+
+public class FragmentSearch extends Fragment {
+ public static String TAG= FragmentSearch.class.getName();
+ private FragmentAddCityBinding binding;
+ private AdapterSearch adapterSearch;
+ private Handler typeingHandler;
+ private TextWatcher txtWatcher;
+ private String cityName="";
+ private Runnable searchRunnable=new Runnable() {
+ @Override
+ public void run() {
+ binding.pbLoading.setVisibility(View.VISIBLE);
+ ControllerData.Companion.getInstances().searchCity(new ModelParamCurrentWeatherByName(cityName.toString(), ControllerAPI.Companion.getInstance().getAPI_KEY()), new INFSearchCity() {
+ @Override
+ public void onSuccess(@NotNull ModelCurrentWeater model) {
+ Log.d("Search","done : "+model.getName());
+ if (getView()!=null && model!=null)
+ Utils.runOnUiThread(() -> {
+ binding.pbLoading.setVisibility(View.GONE);
+ adapterSearch.update(model);
+ });
+ }
+
+ @Override
+ public void onFail() {
+ Log.d("Search"," Fail ");
+ }
+ });
+ }
+ };
+ public static FragmentSearch getInstance(){
+ return new FragmentSearch();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ initView();
+ initValue();
+ }
+
+
+ /**
+ *
+ */
+ private void initView(){
+ adapterSearch=new AdapterSearch(getContext());
+ binding.rclMain.setLayoutManager(new LinearLayoutManager(getContext()));
+ binding.rclMain.setAdapter(adapterSearch);
+ }
+
+ private void initValue(){
+ typeingHandler=new Handler();
+ txtWatcher=new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ Log.d("Search","Typing ");
+ typeingHandler.removeCallbacks(searchRunnable);
+ cityName=s.toString();
+ if (!cityName.isEmpty())
+ typeingHandler.postDelayed(searchRunnable,1500);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ };
+ binding.edtSearch.addTextChangedListener(txtWatcher);
+
+ binding.fabAdd.setOnClickListener(v -> {
+ if (adapterSearch.getModel()!=null){
+ ControllerData.Companion.getInstances().saveCity(adapterSearch.getModel(),false);
+ ControllerData.Companion.getInstances().addToCitiesList(adapterSearch.getModel());
+ ((MainActivity)getActivity()).onBackPressed();
+ }
+ });
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = DataBindingUtil.inflate(inflater, R.layout.fragment_add_city, container, false);
+ return binding.getRoot();
+ }
+
+
+
+}
diff --git a/app/src/main/res/drawable-hdpi/ic_action_add.png b/app/src/main/res/drawable-hdpi/ic_action_add.png
new file mode 100644
index 00000000..565f2f6a
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_add.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_name.png b/app/src/main/res/drawable-hdpi/ic_action_name.png
new file mode 100644
index 00000000..f3b2d85b
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_name.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_add.png b/app/src/main/res/drawable-mdpi/ic_action_add.png
new file mode 100644
index 00000000..f4c62e5b
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_add.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_name.png b/app/src/main/res/drawable-mdpi/ic_action_name.png
new file mode 100644
index 00000000..39dcd42b
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_name.png differ
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..1f6bb290
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_add.png b/app/src/main/res/drawable-xhdpi/ic_action_add.png
new file mode 100644
index 00000000..06323824
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_add.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_name.png b/app/src/main/res/drawable-xhdpi/ic_action_name.png
new file mode 100644
index 00000000..0ebf9ba6
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_name.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_add.png b/app/src/main/res/drawable-xxhdpi/ic_action_add.png
new file mode 100644
index 00000000..bd6269d1
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_add.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_name.png b/app/src/main/res/drawable-xxhdpi/ic_action_name.png
new file mode 100644
index 00000000..59d128d4
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_name.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_add.png b/app/src/main/res/drawable-xxxhdpi/ic_action_add.png
new file mode 100644
index 00000000..5dcf5188
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_add.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_name.png b/app/src/main/res/drawable-xxxhdpi/ic_action_name.png
new file mode 100644
index 00000000..6b7593ad
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_name.png differ
diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml
new file mode 100644
index 00000000..16b6ed2d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,9 @@
+
+
+
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..0d025f9b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_celsius.xml b/app/src/main/res/drawable/ic_wi_celsius.xml
new file mode 100644
index 00000000..fcc7b0ce
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_celsius.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_celsius_black.xml b/app/src/main/res/drawable/ic_wi_celsius_black.xml
new file mode 100644
index 00000000..5d05632d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_celsius_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_cloud.xml b/app/src/main/res/drawable/ic_wi_cloud.xml
new file mode 100644
index 00000000..a9429a3d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_cloud.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_cloud_black.xml b/app/src/main/res/drawable/ic_wi_cloud_black.xml
new file mode 100644
index 00000000..f9e527cc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_cloud_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_cloudy_gusts.xml b/app/src/main/res/drawable/ic_wi_cloudy_gusts.xml
new file mode 100644
index 00000000..db629df7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_cloudy_gusts.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_cloudy_gusts_black.xml b/app/src/main/res/drawable/ic_wi_cloudy_gusts_black.xml
new file mode 100644
index 00000000..624ae9a2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_cloudy_gusts_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_cloudy_windy.xml b/app/src/main/res/drawable/ic_wi_cloudy_windy.xml
new file mode 100644
index 00000000..1569f43c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_cloudy_windy.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_cloudy_windy_black.xml b/app/src/main/res/drawable/ic_wi_cloudy_windy_black.xml
new file mode 100644
index 00000000..bcbb11c8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_cloudy_windy_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_light_wind.xml b/app/src/main/res/drawable/ic_wi_day_light_wind.xml
new file mode 100644
index 00000000..11c949a4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_light_wind.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_light_wind_black.xml b/app/src/main/res/drawable/ic_wi_day_light_wind_black.xml
new file mode 100644
index 00000000..a51cadd8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_light_wind_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_rain.xml b/app/src/main/res/drawable/ic_wi_day_rain.xml
new file mode 100644
index 00000000..90a6019d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_rain.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_rain_black.xml b/app/src/main/res/drawable/ic_wi_day_rain_black.xml
new file mode 100644
index 00000000..3c43e14a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_rain_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_snow_wind.xml b/app/src/main/res/drawable/ic_wi_day_snow_wind.xml
new file mode 100644
index 00000000..9d61385c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_snow_wind.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_snow_wind_black.xml b/app/src/main/res/drawable/ic_wi_day_snow_wind_black.xml
new file mode 100644
index 00000000..6c8dcb50
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_snow_wind_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_sunny.xml b/app/src/main/res/drawable/ic_wi_day_sunny.xml
new file mode 100644
index 00000000..c8bee6d9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_sunny.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_day_sunny_black.xml b/app/src/main/res/drawable/ic_wi_day_sunny_black.xml
new file mode 100644
index 00000000..838787bf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_day_sunny_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_lightning.xml b/app/src/main/res/drawable/ic_wi_lightning.xml
new file mode 100644
index 00000000..564483e0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_lightning.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_lightning_black.xml b/app/src/main/res/drawable/ic_wi_lightning_black.xml
new file mode 100644
index 00000000..ddfac178
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_lightning_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_night_alt_cloudy_windy.xml b/app/src/main/res/drawable/ic_wi_night_alt_cloudy_windy.xml
new file mode 100644
index 00000000..22abcbad
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_night_alt_cloudy_windy.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_night_alt_rain.xml b/app/src/main/res/drawable/ic_wi_night_alt_rain.xml
new file mode 100644
index 00000000..d2787095
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_night_alt_rain.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_night_alt_snow.xml b/app/src/main/res/drawable/ic_wi_night_alt_snow.xml
new file mode 100644
index 00000000..34fb55bf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_night_alt_snow.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_night_clear.xml b/app/src/main/res/drawable/ic_wi_night_clear.xml
new file mode 100644
index 00000000..d72f637e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_night_clear.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wi_night_cloudy.xml b/app/src/main/res/drawable/ic_wi_night_cloudy.xml
new file mode 100644
index 00000000..69641e46
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wi_night_cloudy.xml
@@ -0,0 +1,9 @@
+
+
+
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..e03e8bb2
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_detail.xml b/app/src/main/res/layout/adapter_detail.xml
new file mode 100644
index 00000000..0b13e145
--- /dev/null
+++ b/app/src/main/res/layout/adapter_detail.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_forecast.xml b/app/src/main/res/layout/adapter_forecast.xml
new file mode 100644
index 00000000..26c3e826
--- /dev/null
+++ b/app/src/main/res/layout/adapter_forecast.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_main.xml b/app/src/main/res/layout/adapter_main.xml
new file mode 100644
index 00000000..cfad9dae
--- /dev/null
+++ b/app/src/main/res/layout/adapter_main.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_search.xml b/app/src/main/res/layout/adapter_search.xml
new file mode 100644
index 00000000..4ebe6e4f
--- /dev/null
+++ b/app/src/main/res/layout/adapter_search.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_add_city.xml b/app/src/main/res/layout/fragment_add_city.xml
new file mode 100644
index 00000000..7297cafc
--- /dev/null
+++ b/app/src/main/res/layout/fragment_add_city.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_detail.xml b/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 00000000..952c3e36
--- /dev/null
+++ b/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 00000000..497cc24f
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..898f3ed5
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dffca360
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..64ba76f7
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dae5e082
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..e5ed4659
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..14ed0af3
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b0907cac
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..d8ae0315
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..2c18de9e
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..beed3cdd
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..206c0614
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,30 @@
+
+
+ #673AB7
+ #512DA8
+ #D1C4E9
+ #FFC107
+ #212121
+ #757575
+ #FFFFFF
+ #BDBDBD
+
+ #FFFFFF
+ #000000
+
+ #6D5DF1
+ #FFC107
+ #757575
+ #757575
+ #9E9E9E
+ #795548
+ #BDBDBD
+ #455A64
+ #BDBDBD
+ #BDBDBD
+ #BDBDBD
+ #BDBDBD
+ #0097A7
+ #00BCD4
+ #303F9F
+
diff --git a/app/src/main/res/values/dimension.xml b/app/src/main/res/values/dimension.xml
new file mode 100644
index 00000000..fb72bf74
--- /dev/null
+++ b/app/src/main/res/values/dimension.xml
@@ -0,0 +1,8 @@
+
+
+ 16sp
+ 16sp
+ 14sp
+ 34sp
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..86d3d536
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,17 @@
+
+ weatherforecast
+ We need your location in order to show weather!
+ Humidity : %1$s
+ Wind : %1$s km/h
+ Pressure : %1$s
+ MinTemp : %1$s c
+ MaxTemp : %1$s c
+
+ City
+
+ Delete City
+ Are you sure you want to delete this city?
+
+ Yes
+ No
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..15fa6861
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/app/src/test/java/com/midnight/weatherforecast/ConditionCheck.java b/app/src/test/java/com/midnight/weatherforecast/ConditionCheck.java
new file mode 100644
index 00000000..68192c00
--- /dev/null
+++ b/app/src/test/java/com/midnight/weatherforecast/ConditionCheck.java
@@ -0,0 +1,11 @@
+package com.midnight.weatherforecast;
+
+/**
+ * Created by midnight on 2/13/18.
+ */
+
+public interface ConditionCheck {
+ public boolean condition();
+
+ public void finalyAssert();
+}
diff --git a/app/src/test/java/com/midnight/weatherforecast/Halt.java b/app/src/test/java/com/midnight/weatherforecast/Halt.java
new file mode 100644
index 00000000..f762370c
--- /dev/null
+++ b/app/src/test/java/com/midnight/weatherforecast/Halt.java
@@ -0,0 +1,61 @@
+package com.midnight.weatherforecast;
+
+/**
+ * Created by midnight on 2/13/18.
+ */
+
+public class Halt {
+ long timeout, interval;
+ final long TIME_OUT, INTERVAL;
+ private ConditionCheck conditionCheck;
+ public Halt(long timeout, long interval) {
+ this.timeout = timeout;
+ this.interval = interval;
+ TIME_OUT = timeout;
+ INTERVAL = interval;
+ }
+
+
+ public void execHalt(ConditionCheck condition_Check) {
+ try {
+ conditionCheck=condition_Check;
+ while (timeout > 0 && conditionCheck.condition()) {
+ Thread.sleep(interval);
+ timeout = timeout - interval;
+ }
+ } catch (InterruptedException e) {
+
+ } finally {
+ conditionCheck.finalyAssert();
+ }
+
+ }
+
+ public void reset() {
+ this.timeout = TIME_OUT;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+
+ public long getInterval() {
+ return interval;
+ }
+
+ public void setInterval(long interval) {
+ this.interval = interval;
+ }
+
+ public ConditionCheck getConditionCheck() {
+ return conditionCheck;
+ }
+
+ public void setConditionCheck(ConditionCheck conditionCheck) {
+ this.conditionCheck = conditionCheck;
+ }
+}
diff --git a/app/src/test/java/com/midnight/weatherforecast/MyNetworkSecurityPolicy.java b/app/src/test/java/com/midnight/weatherforecast/MyNetworkSecurityPolicy.java
new file mode 100644
index 00000000..73078cd9
--- /dev/null
+++ b/app/src/test/java/com/midnight/weatherforecast/MyNetworkSecurityPolicy.java
@@ -0,0 +1,28 @@
+package com.midnight.weatherforecast;
+
+import android.security.NetworkSecurityPolicy;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(NetworkSecurityPolicy.class)
+public class MyNetworkSecurityPolicy {
+
+ public MyNetworkSecurityPolicy() {
+ }
+
+ @Implementation
+ public static NetworkSecurityPolicy getInstance() {
+ try {
+ Class> shadow = MyNetworkSecurityPolicy.class.forName("android.security.NetworkSecurityPolicy");
+ return (NetworkSecurityPolicy) shadow.newInstance();
+ } catch (Exception e) {
+ throw new AssertionError();
+ }
+ }
+
+ @Implementation
+ public boolean isCleartextTrafficPermitted() {
+ return true;
+ }
+}
diff --git a/app/src/test/java/com/midnight/weatherforecast/TestGetCurrentCityWeather.java b/app/src/test/java/com/midnight/weatherforecast/TestGetCurrentCityWeather.java
new file mode 100644
index 00000000..eb1d2c1e
--- /dev/null
+++ b/app/src/test/java/com/midnight/weatherforecast/TestGetCurrentCityWeather.java
@@ -0,0 +1,137 @@
+package com.midnight.weatherforecast;
+
+import android.os.Build;
+import android.os.Looper;
+
+import com.midnight.weatherforecast.controller.ControllerAPI;
+import com.midnight.weatherforecast.core.Loader;
+import com.midnight.weatherforecast.interfaces.INFApi;
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByGeo;
+import com.midnight.weatherforecast.models.modelsParam.ModelParamCurrentWeatherByName;
+import com.midnight.weatherforecast.models.modelsResponse.ModelCurrentWeater;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+import okhttp3.mockwebserver.MockWebServer;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+import static org.robolectric.Shadows.shadowOf;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = Build.VERSION_CODES.M,application = Loader.class,shadows = MyNetworkSecurityPolicy.class)
+public class TestGetCurrentCityWeather {
+ private String finalResult="";
+ private MockWebServer mockWebServer=new MockWebServer();
+ private INFApi apiService;
+ @Before
+ public void startTest(){
+ try {
+ mockWebServer.start();
+ apiService = new Retrofit.Builder()
+ .baseUrl(mockWebServer.url("/"))
+ .build()
+ .create(INFApi.class);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+ @Test
+ public void getCurrentByName() {
+ Halt checker=new Halt(10000,1000);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ ControllerAPI.Companion.getInstance().getCurrentWeatherByName(new ModelParamCurrentWeatherByName("Stockholm", "62fc4256-8f8c-11e5-8994-feff819cdc9f"), new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ finalResult=response.body().getName();
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ finalResult="-1";
+ }
+ });
+
+ checker.execHalt(new ConditionCheck() {
+ @Override
+ public boolean condition() {
+ if (finalResult.isEmpty()){
+ shadowOf(Looper.getMainLooper()).idle();
+ return true;
+ }
+ shadowOf(Looper.getMainLooper()).idle();
+ return false;
+ }
+
+ @Override
+ public void finalyAssert() {
+ Assert.assertEquals("Stockholm",finalResult);
+ }
+ });
+
+ Robolectric.flushForegroundThreadScheduler();
+ Robolectric.flushBackgroundThreadScheduler();
+ }
+
+ @Test
+ public void getCurrentByGeo() {
+ Halt checker=new Halt(10000,1000);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ ControllerAPI.Companion.getInstance().getCurrentWeatherByGPS(new ModelParamCurrentWeatherByGeo("59.33","18.07", "62fc4256-8f8c-11e5-8994-feff819cdc9f"), new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ finalResult=response.body().getName();
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ finalResult="-1";
+ }
+ });
+
+ checker.execHalt(new ConditionCheck() {
+ @Override
+ public boolean condition() {
+ if (finalResult.isEmpty()){
+ shadowOf(Looper.getMainLooper()).idle();
+ return true;
+ }
+ shadowOf(Looper.getMainLooper()).idle();
+ return false;
+ }
+
+ @Override
+ public void finalyAssert() {
+ Assert.assertEquals("Stockholm",finalResult);
+ }
+ });
+
+ Robolectric.flushForegroundThreadScheduler();
+ Robolectric.flushBackgroundThreadScheduler();
+ }
+
+ @After
+ public void endTest(){
+ try {
+ mockWebServer.shutdown();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..4567937a
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,32 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext {
+ kotlin_version = '1.3.50'
+ }
+ repositories {
+ google()
+ jcenter()
+
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.1'
+ classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..199d16ed
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..add10aa9
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Oct 14 12:13:30 CEST 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..cccdd3d5
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..03890672
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name='weatherforecast'