- Popup for google playstore permissions added

- UI tests with stubbing added
- linting clean ups
- changes to fragments and base fragment

Took 3 hours 57 minutes
This commit is contained in:
2022-06-09 22:48:33 +01:00
parent 742875c295
commit 03c11633d8
21 changed files with 280 additions and 188 deletions

97
.gitignore vendored
View File

@@ -1,11 +1,90 @@
*.iml
### AndroidStudio ###
# Covers files to be ignored for android development using Android Studio.
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
/.idea/caches
.DS_Store
/build
/captures
.gradle/
build/
# Signing files
.signing/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio
/*/build/
/*/local.properties
/*/out
/*/*/build
/*/*/production
captures/
.navigation/
*.ipr
*~
*.swp
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Android Patch
gen-external-apklibs
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
/projectFilesBackup
# IntelliJ IDEA
*.iml
*.iws
/out/
# User-specific configurations
.idea/caches/
.idea/libraries/
.idea/shelf/
.idea/workspace.xml
.idea/tasks.xml
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
.idea/datasources.xml
.idea/dataSources.ids
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
.idea/assetWizardSettings.xml
.idea/gradle.xml
.idea/jarRepositorie

Binary file not shown.

20
.idea/gradle.xml generated
View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

48
.idea/misc.xml generated
View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="12">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="android.annotation.Nullable" />
<item index="7" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="11">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="android.annotation.NonNull" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

11
.idea/modules.xml generated
View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Altas_-_Weather.iml" filepath="$PROJECT_DIR$/.idea/modules/Altas_-_Weather.iml" group="Altas_-_Weather" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Altas_-_Weather-app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Altas_-_Weather-app.iml" group="Altas_-_Weather/app" />
<module fileurl="file://$PROJECT_DIR$/Weather_app.iml" filepath="$PROJECT_DIR$/Weather_app.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@@ -97,6 +97,7 @@ dependencies {
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
implementation 'androidx.test.espresso:espresso-idling-resource:3.4.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
// Unit testing
@@ -108,8 +109,8 @@ dependencies {
// android unit testing and espresso
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
implementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test:rules:1.4.1-alpha06'
//mock websever for testing retrofit responses
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
@@ -117,7 +118,6 @@ dependencies {
//mockito and livedata testing
testImplementation 'org.mockito:mockito-inline:2.13.0'
implementation 'android.arch.core:core-testing'
androidTestImplementation 'androidx.test:rules:1.3.0-rc01'
// Mockk
def mockk_ver = "1.10.5"

View File

@@ -1,16 +1,17 @@
package com.appttude.h_mal.atlas_weather.application
import androidx.room.Room
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.idling.CountingIdlingResource
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.data.location.MockLocationProvider
import com.appttude.h_mal.atlas_weather.data.network.Api
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
import com.appttude.h_mal.atlas_weather.data.network.interceptors.MockingNetworkInterceptor
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
import com.appttude.h_mal.atlas_weather.data.room.Converter
import java.io.BufferedReader
class TestAppClass : BaseAppClass() {
@@ -22,17 +23,23 @@ class TestAppClass : BaseAppClass() {
IdlingRegistry.getInstance().register(idlingResources)
}
override fun createNetworkModule(): Api {
override fun createNetworkModule(): WeatherApi {
return NetworkModule().invoke<WeatherApi>(
mockingNetworkInterceptor,
NetworkConnectionInterceptor(this),
QueryParamsInterceptor(),
loggingInterceptor,
mockingNetworkInterceptor
)
loggingInterceptor
) as WeatherApi
}
override fun createLocationModule() = MockLocationProvider()
override fun createRoomDatabase(): AppDatabase {
return Room.inMemoryDatabaseBuilder(this, AppDatabase::class.java)
.addTypeConverter(Converter(this))
.build()
}
fun stubUrl(url: String, rawPath: String) {
val id = resources.getIdentifier(rawPath, "raw", packageName)
val iStream = resources.openRawResource(id)
@@ -40,7 +47,7 @@ class TestAppClass : BaseAppClass() {
mockingNetworkInterceptor.addUrlStub(url = url, data = data)
}
fun removeUrlStub(url: String){
fun removeUrlStub(url: String) {
mockingNetworkInterceptor.removeUrlStub(url = url)
}

View File

@@ -1,5 +1,12 @@
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
import android.content.Intent
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.pressBack
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import com.appttude.h_mal.atlas_weather.application.TestAppClass
@@ -21,6 +28,12 @@ open class BaseTest {
testApp = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
setupFeed()
}
override fun afterActivityLaunched() {
// Dismiss dialog
onView(withText("AGREE")).inRoot(isDialog()).check(matches(isDisplayed())).perform(ViewActions.click())
}
}
fun stubEndpoint(url: String, stub: Stubs) {
@@ -32,8 +45,7 @@ open class BaseTest {
}
@After
fun tearDown() {
}
fun tearDown() {}
open fun setupFeed() {}
}

View File

@@ -1,6 +1,11 @@
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.RootMatchers
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.rule.GrantPermissionRule
import com.appttude.h_mal.atlas_weather.monoWeather.robot.homeScreen
import com.appttude.h_mal.atlas_weather.utils.Stubs

View File

@@ -0,0 +1,85 @@
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ActivityScenario.launch
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.RootMatchers
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.GrantPermissionRule
import com.appttude.h_mal.atlas_weather.application.TestAppClass
import com.appttude.h_mal.atlas_weather.monoWeather.robot.homeScreen
import com.appttude.h_mal.atlas_weather.monoWeather.ui.MainActivity
import com.appttude.h_mal.atlas_weather.utils.Stubs
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class HomePageUITestScenario : BaseMainScenario() {
@Rule
@JvmField
var mGrantPermissionRule: GrantPermissionRule =
GrantPermissionRule.grant(
"android.permission.ACCESS_COARSE_LOCATION")
override fun setupFeed() {
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Valid)
}
@Test
fun loadApp_validWeatherResponse_returnsValidPage() {
homeScreen {
verifyCurrentTemperature(2)
verifyCurrentLocation("Mock Location")
}
}
}
open class BaseMainScenario {
lateinit var scenario: ActivityScenario<MainActivity>
private lateinit var testApp : TestAppClass
@Before
fun setUp() {
scenario = launch(MainActivity::class.java)
scenario.moveToState(Lifecycle.State.INITIALIZED)
scenario.onActivity {
runBlocking {
testApp = it.application as TestAppClass
setupFeed()
}
}
scenario.moveToState(Lifecycle.State.CREATED).onActivity {
Espresso.onView(ViewMatchers.withText("AGREE"))
.inRoot(RootMatchers.isDialog())
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
.perform(ViewActions.click())
}
}
fun stubEndpoint(url: String, stub: Stubs) {
testApp.stubUrl(url, stub.id)
}
fun unstubEndpoint(url: String) {
testApp.removeUrlStub(url)
}
@After
fun tearDown() {}
open fun setupFeed() {}
}

View File

@@ -1,5 +1,6 @@
package com.appttude.h_mal.atlas_weather.application
import androidx.room.RoomDatabase
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
import com.appttude.h_mal.atlas_weather.data.network.Api
@@ -26,14 +27,16 @@ const val LOCATION_PERMISSION_REQUEST = 505
class AppClass : BaseAppClass() {
override fun createNetworkModule(): Api {
override fun createNetworkModule(): WeatherApi {
return NetworkModule().invoke<WeatherApi>(
NetworkConnectionInterceptor(this),
QueryParamsInterceptor(),
loggingInterceptor
)
) as WeatherApi
}
override fun createLocationModule() = LocationProviderImpl(this)
override fun createRoomDatabase(): AppDatabase = AppDatabase(this)
}

View File

@@ -1,15 +1,8 @@
package com.appttude.h_mal.atlas_weather.application
import android.app.Application
import androidx.test.espresso.idling.CountingIdlingResource
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
import com.appttude.h_mal.atlas_weather.data.network.Api
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
@@ -31,11 +24,11 @@ abstract class BaseAppClass : Application(), KodeinAware {
override val kodein = Kodein.lazy {
import(androidXModule(this@BaseAppClass))
bind() from singleton { createNetworkModule() as WeatherApi}
bind() from singleton { createNetworkModule() }
bind() from singleton { createLocationModule() }
bind() from singleton { Gson() }
bind() from singleton { AppDatabase(instance()) }
bind() from singleton { createRoomDatabase() }
bind() from singleton { PreferenceProvider(instance()) }
bind() from singleton { RepositoryImpl(instance(), instance(), instance()) }
bind() from singleton { SettingsRepositoryImpl(instance()) }
@@ -43,7 +36,8 @@ abstract class BaseAppClass : Application(), KodeinAware {
bind() from provider { ApplicationViewModelFactory(instance(), instance()) }
}
abstract fun createNetworkModule() : Api
abstract fun createLocationModule() : LocationProvider
abstract fun createNetworkModule(): WeatherApi
abstract fun createLocationModule(): LocationProvider
abstract fun createRoomDatabase(): AppDatabase
}

View File

@@ -10,19 +10,18 @@ import org.kodein.di.android.kodein
import org.kodein.di.generic.instance
@ProvidedTypeConverter
class Converter(context: Context): KodeinAware{
class Converter(context: Context) : KodeinAware {
override val kodein by kodein(context)
private val gson by instance<Gson>()
@TypeConverter
fun fullWeatherToString(fullWeather: FullWeather): String{
fun fullWeatherToString(fullWeather: FullWeather): String {
return gson.toJson(fullWeather)
}
@TypeConverter
fun stringToFullWeather(string: String): FullWeather{
fun stringToFullWeather(string: String): FullWeather {
return gson.fromJson(string, FullWeather::class.java)
}
}

View File

@@ -20,7 +20,7 @@ abstract class BaseDeclarationDialog(val context: Context): DeclarationBuilder {
abstract override val link: String
abstract override val message: String
fun showDialog(agreeCallback: () -> Unit = { Unit }, disagreeCallback: () -> Unit = { Unit }) {
fun showDialog(agreeCallback: () -> Unit = { }, disagreeCallback: () -> Unit = { Unit }) {
val myMessage = buildMessage()
val builder = AlertDialog.Builder(context)

View File

@@ -2,15 +2,18 @@ package com.appttude.h_mal.atlas_weather.monoWeather.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.annotation.LayoutRes
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
import com.appttude.h_mal.atlas_weather.utils.Event
import com.appttude.h_mal.atlas_weather.utils.displayToast
import com.appttude.h_mal.atlas_weather.utils.hide
@@ -24,7 +27,7 @@ import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
import kotlin.properties.Delegates
abstract class BaseFragment : Fragment(), KodeinAware {
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId), KodeinAware {
override val kodein by kodein()
val factory by instance<ApplicationViewModelFactory>()
@@ -119,4 +122,21 @@ abstract class BaseFragment : Fragment(), KodeinAware {
}
}
@SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == LOCATION_PERMISSION_REQUEST) {
if (grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionsGranted()
displayToast("Permission granted")
} else {
permissionsRefused()
displayToast("Permission denied")
}
}
}
open fun permissionsGranted() {}
open fun permissionsRefused() {}
}

View File

@@ -14,7 +14,7 @@ import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import kotlinx.android.synthetic.main.fragment_home.*
class WorldItemFragment : BaseFragment() {
class WorldItemFragment : BaseFragment(R.layout.fragment_home) {
private val viewModel by getFragmentViewModel<WorldViewModel>()
private var param1: String? = null
@@ -24,12 +24,6 @@ class WorldItemFragment : BaseFragment() {
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).locationName
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View File

@@ -1,6 +1,7 @@
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home
import android.Manifest
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
@@ -11,6 +12,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.observe
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
import com.appttude.h_mal.atlas_weather.monoWeather.dialog.PermissionsDeclarationDialog
import com.appttude.h_mal.atlas_weather.monoWeather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.WeatherRecyclerAdapter
@@ -24,36 +26,29 @@ import kotlinx.android.synthetic.main.fragment_home.*
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class HomeFragment : BaseFragment() {
class HomeFragment : BaseFragment(R.layout.fragment_home) {
private val viewModel by getFragmentViewModel<MainViewModel>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
@SuppressLint("MissingPermission")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerAdapter = WeatherRecyclerAdapter {
val directions =
HomeFragmentDirections.actionHomeFragmentToFurtherDetailsFragment(it)
navigateTo(directions)
}
val recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
navigateToFurtherDetails(it)
})
forecast_listview.adapter = recyclerAdapter
PermissionsDeclarationDialog(requireContext()).showDialog(agreeCallback = {
getPermissionResult(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
viewModel.fetchData()
}
})
swipe_refresh.apply {
setOnRefreshListener {
getPermissionResult(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
viewModel.fetchData()
isRefreshing = true
}
@@ -70,16 +65,13 @@ class HomeFragment : BaseFragment() {
}
@SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == LOCATION_PERMISSION_REQUEST) {
if (grantResults.isNotEmpty()
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
viewModel.fetchData()
displayToast("Permission granted")
} else {
displayToast("Permission denied")
}
}
override fun permissionsGranted() {
viewModel.fetchData()
}
private fun navigateToFurtherDetails(forecast: Forecast){
val directions = HomeFragmentDirections
.actionHomeFragmentToFurtherDetailsFragment(forecast)
navigateTo(directions)
}
}

View File

@@ -14,16 +14,10 @@ import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import kotlinx.android.synthetic.main.activity_add_forecast.*
class AddLocationFragment : BaseFragment() {
class AddLocationFragment : BaseFragment(R.layout.activity_add_forecast) {
private val viewModel by getFragmentViewModel<WorldViewModel>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.activity_add_forecast, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View File

@@ -22,7 +22,7 @@ import kotlinx.android.synthetic.main.fragment_add_location.world_recycler
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class WorldFragment : BaseFragment() {
class WorldFragment : BaseFragment(R.layout.fragment__two) {
private val viewModel by getFragmentViewModel<WorldViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
@@ -31,12 +31,6 @@ class WorldFragment : BaseFragment() {
viewModel.fetchAllLocations()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment__two, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View File

@@ -12,8 +12,8 @@ import com.appttude.h_mal.atlas_weather.utils.generateView
import com.appttude.h_mal.atlas_weather.utils.loadImage
class WorldRecyclerAdapter(
val itemClick: (WeatherDisplay) -> Unit,
val itemLongClick: (String) -> Unit
private val itemClick: (WeatherDisplay) -> Unit,
private val itemLongClick: (String) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var weather: MutableList<WeatherDisplay> = mutableListOf()
@@ -78,23 +78,28 @@ class WorldRecyclerAdapter(
return if (weather.size == 0) 1 else weather.size
}
internal class WorldHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
internal class WorldHolderCurrent(cellView: View) : BaseViewHolder<WeatherDisplay>(cellView) {
var locationTV: TextView = listItemView.findViewById(R.id.db_location)
var conditionTV: TextView = listItemView.findViewById(R.id.db_condition)
var weatherIV: ImageView = listItemView.findViewById(R.id.db_icon)
var avgTempTV: TextView = listItemView.findViewById(R.id.db_main_temp)
var tempUnit: TextView = listItemView.findViewById(R.id.db_temp_unit)
private val locationTV: TextView = cellView.findViewById(R.id.db_location)
private val conditionTV: TextView = cellView.findViewById(R.id.db_condition)
private val weatherIV: ImageView = cellView.findViewById(R.id.db_icon)
private val avgTempTV: TextView = cellView.findViewById(R.id.db_main_temp)
private val tempUnit: TextView = cellView.findViewById(R.id.db_temp_unit)
fun bindData(weather: WeatherDisplay?){
locationTV.text = weather?.displayName
conditionTV.text = weather?.description
weatherIV.loadImage(weather?.iconURL)
avgTempTV.text = weather?.forecast?.get(0)?.mainTemp
override fun bindData(data: WeatherDisplay?){
locationTV.text = data?.displayName
conditionTV.text = data?.description
weatherIV.loadImage(data?.iconURL)
avgTempTV.text = data?.forecast?.get(0)?.mainTemp
tempUnit.text = itemView.context.getString(R.string.degrees)
}
}
abstract class BaseViewHolder<T : Any>(cellView: View) : RecyclerView.ViewHolder(cellView) {
abstract fun bindData(data : T?)
}
}