mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
- Testsuite expansion
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -65,6 +65,7 @@ gen-external-apklibs
|
||||
/out/
|
||||
|
||||
# User-specific configurations
|
||||
.idea/androidTestResultsUserPreferences.xml
|
||||
.idea/caches/
|
||||
.idea/libraries/
|
||||
.idea/shelf/
|
||||
@@ -94,5 +95,3 @@ gen-external-apklibs
|
||||
/fastlane/report.xml
|
||||
# Google play files
|
||||
/google-play-key.json
|
||||
|
||||
/.idea/androidTestResultsUserPreferences.xml
|
||||
|
||||
237
.idea/androidTestResultsUserPreferences.xml
generated
237
.idea/androidTestResultsUserPreferences.xml
generated
@@ -1,237 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidTestResultsUserPreferences">
|
||||
<option name="androidTestResultsTableState">
|
||||
<map>
|
||||
<entry key="-2146704034">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-2113309033">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="2C121FDH300122" value="120" />
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Google Android SDK built for x86" value="120" />
|
||||
<entry key="Google Pixel 7 Pro" value="120" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
<entry key="emulator-5554" value="120" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-2008434490">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-1906103057">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-1721686438">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-1578868619">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-860247611">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="2C121FDH300122" value="120" />
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Google Pixel 7 Pro" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-409920851">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="108569748">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="2C121FDH300122" value="120" />
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Google Pixel 7 Pro" value="120" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="110413981">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="170536241">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="287238248">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="408375334">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="721647317">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="1127175145">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="1256180664">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="1440597283">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
218
Gemfile.lock
Normal file
218
Gemfile.lock
Normal file
@@ -0,0 +1,218 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.6)
|
||||
rexml
|
||||
addressable (2.8.5)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.798.0)
|
||||
aws-sdk-core (3.180.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.71.0)
|
||||
aws-sdk-core (~> 3, >= 3.177.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.132.0)
|
||||
aws-sdk-core (~> 3, >= 3.179.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.6)
|
||||
aws-sigv4 (1.6.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.5)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.100.0)
|
||||
faraday (1.10.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.7)
|
||||
fastlane (2.214.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.46.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.17.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.13.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-storage_v1 (0.19.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.3.1)
|
||||
google-cloud-storage (1.44.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.19.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.7.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.5)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.6.2)
|
||||
json (2.6.3)
|
||||
jwt (2.7.1)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.3.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
optparse (0.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.7.0)
|
||||
public_suffix (5.0.3)
|
||||
rake (13.0.6)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.6)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.17.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.8.1)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.22.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.5
|
||||
@@ -15,6 +15,7 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
@Test
|
||||
fun loadApp_invalidKeyWeatherResponse_returnsEmptyViewPage() {
|
||||
homeScreen {
|
||||
waitFor(2000)
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
}
|
||||
@@ -23,6 +24,7 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
@Test
|
||||
fun invalidKeyWeatherResponse_swipeToRefresh_returnsValidPage() {
|
||||
homeScreen {
|
||||
waitFor(2000)
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
|
||||
|
||||
@@ -34,15 +34,6 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
|
||||
navigateToFurtherDetails(it)
|
||||
})
|
||||
|
||||
forecast_listview.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = recyclerAdapter
|
||||
}
|
||||
|
||||
swipe_refresh.apply {
|
||||
setOnRefreshListener {
|
||||
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
|
||||
@@ -51,16 +42,14 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(error: Any?) {
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
|
||||
navigateToFurtherDetails(it)
|
||||
})
|
||||
|
||||
override fun onSuccess(data: Any?) {
|
||||
swipe_refresh.isRefreshing = false
|
||||
if (data is WeatherDisplay) {
|
||||
recyclerAdapter.addCurrent(data)
|
||||
forecast_listview.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = recyclerAdapter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +62,19 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Any?) {
|
||||
super.onSuccess(data)
|
||||
swipe_refresh.isRefreshing = false
|
||||
if (data is WeatherDisplay) {
|
||||
recyclerAdapter.addCurrent(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(error: Any?) {
|
||||
super.onFailure(error)
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun permissionsGranted() {
|
||||
viewModel.fetchData()
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast.ViewHolderForecast
|
||||
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecastDaily.ViewHolderForecastDaily
|
||||
|
||||
class WeatherRecyclerAdapter(
|
||||
val itemClick: (Forecast) -> Unit
|
||||
private val itemClick: (Forecast) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var weather: WeatherDisplay? = null
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun addCurrent(current: WeatherDisplay) {
|
||||
weather = current
|
||||
notifyDataSetChanged()
|
||||
@@ -32,8 +31,8 @@ class WeatherRecyclerAdapter(
|
||||
ViewHolderCurrent(viewCurrent)
|
||||
}
|
||||
|
||||
is ViewType.Forecast -> {
|
||||
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
||||
is ViewType.ForecastHourly -> {
|
||||
val viewForecast = parent.generateView(R.layout.hourly_item_forecast)
|
||||
ViewHolderForecast(viewForecast)
|
||||
}
|
||||
|
||||
@@ -41,13 +40,19 @@ class WeatherRecyclerAdapter(
|
||||
val viewFurther = parent.generateView(R.layout.list_item_further)
|
||||
ViewHolderFurtherDetails(viewFurther)
|
||||
}
|
||||
|
||||
is ViewType.ForecastDaily -> {
|
||||
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
||||
ViewHolderForecastDaily(viewForecast)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ViewType {
|
||||
object Empty : ViewType()
|
||||
object Current : ViewType()
|
||||
object Forecast : ViewType()
|
||||
object ForecastHourly : ViewType()
|
||||
object ForecastDaily : ViewType()
|
||||
object Further : ViewType()
|
||||
}
|
||||
|
||||
@@ -55,19 +60,20 @@ class WeatherRecyclerAdapter(
|
||||
return when (type) {
|
||||
0 -> ViewType.Empty
|
||||
1 -> ViewType.Current
|
||||
2 -> ViewType.Forecast
|
||||
2 -> ViewType.ForecastHourly
|
||||
3 -> ViewType.Further
|
||||
4 -> ViewType.ForecastDaily
|
||||
else -> ViewType.Empty
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (weather == null) return 0
|
||||
|
||||
return when (position) {
|
||||
0 -> 1
|
||||
in 1 until itemCount - 2 -> 2
|
||||
itemCount - 1 -> 3
|
||||
1 -> 3
|
||||
2 -> 2
|
||||
in 3 until (itemCount) -> 4
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@@ -84,28 +90,31 @@ class WeatherRecyclerAdapter(
|
||||
viewHolderCurrent.bindData(weather)
|
||||
}
|
||||
|
||||
is ViewType.Forecast -> {
|
||||
val viewHolderForecast = holder as ViewHolderForecast
|
||||
|
||||
weather?.forecast?.get(position - 1)?.let { i ->
|
||||
viewHolderForecast.bindView(i)
|
||||
viewHolderForecast.itemView.setOnClickListener {
|
||||
itemClick(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is ViewType.Further -> {
|
||||
val viewHolderCurrent = holder as ViewHolderFurtherDetails
|
||||
viewHolderCurrent.bindData(weather)
|
||||
}
|
||||
|
||||
is ViewType.ForecastHourly -> {
|
||||
val viewHolderForecast = holder as ViewHolderForecast
|
||||
viewHolderForecast.bindView(weather?.hourly)
|
||||
}
|
||||
|
||||
is ViewType.ForecastDaily -> {
|
||||
val viewHolderForecast = holder as ViewHolderForecastDaily
|
||||
weather?.forecast?.getOrNull(position - 3)?.let { f ->
|
||||
viewHolderForecast.bindView(f)
|
||||
viewHolderForecast.itemView.setOnClickListener {
|
||||
itemClick.invoke(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
if (weather == null) return 0
|
||||
return 2 + (weather?.forecast?.size ?: 0)
|
||||
return if (weather == null) 1 else 3 + (weather?.forecast?.size ?: 0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.appttude.h_mal.monoWeather.ui.home.adapter.forecast
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||
import com.appttude.h_mal.atlas_weather.utils.toTime
|
||||
|
||||
class GridCellHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var dayTV: TextView = itemView.findViewById(R.id.widget_item_day)
|
||||
var weatherIV: ImageView = itemView.findViewById(R.id.widget_item_image)
|
||||
var mainTempTV: TextView = itemView.findViewById(R.id.widget_item_temp_high)
|
||||
|
||||
fun bindView(hour: Hour?) {
|
||||
dayTV.text = hour?.dt?.toTime()
|
||||
weatherIV.loadImage(hour?.icon)
|
||||
mainTempTV.text = hour?.temp?.toInt()?.toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||
import com.appttude.h_mal.monoWeather.ui.home.adapter.forecast.GridCellHolder
|
||||
|
||||
class GridForecastAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var weather: MutableList<Hour> = mutableListOf()
|
||||
|
||||
fun addCurrent(current: List<Hour>?) {
|
||||
weather.clear()
|
||||
current?.let { weather.addAll(it) }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val viewCurrent = parent.generateView(R.layout.hourly_forecast_grid_item)
|
||||
return GridCellHolder(viewCurrent)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val view = holder as GridCellHolder
|
||||
val forecast = weather[position]
|
||||
view.bindView(forecast)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = weather.size
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast.GridForecastAdapter
|
||||
|
||||
class ViewHolderForecast(
|
||||
itemView: View
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var recyclerView: RecyclerView = itemView.findViewById(R.id.forecast_recyclerview)
|
||||
|
||||
fun bindView(forecasts: List<Hour>?) {
|
||||
val adapter = GridForecastAdapter()
|
||||
adapter.addCurrent(forecasts)
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecastDaily
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
@@ -8,21 +8,21 @@ import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||
|
||||
class ViewHolderForecast(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
class ViewHolderForecastDaily(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var dateTV: TextView = itemView.findViewById(R.id.list_date)
|
||||
var dayTV: TextView = itemView.findViewById(R.id.list_day)
|
||||
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
|
||||
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
|
||||
var mainTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
|
||||
var minorTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
|
||||
var maxTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
|
||||
var minTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
|
||||
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
|
||||
|
||||
fun bindView(forecast: Forecast?) {
|
||||
dateTV.text = forecast?.date
|
||||
dayTV.text = forecast?.day
|
||||
conditionTV.text = forecast?.condition
|
||||
weatherIV.loadImage(forecast?.weatherIcon)
|
||||
mainTempTV.text = forecast?.mainTemp
|
||||
minorTempTV.text = forecast?.minorTemp
|
||||
maxTempTV.text = forecast?.mainTemp
|
||||
minTempTV.text = forecast?.minorTemp
|
||||
conditionTV.text = forecast?.condition
|
||||
}
|
||||
}
|
||||
@@ -36,20 +36,4 @@
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -43,17 +43,5 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
tools:layout="@layout/fragment_home" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_circular"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="0.2dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/widget_item_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_item_day"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="#ffffff"
|
||||
tools:text="Dec 1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/widget_item_image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/image_string"
|
||||
tools:src="@drawable/cloud_symbol"
|
||||
tools:tint="@color/colour_one" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_item_temp_high"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="20" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/db_temp_unit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/degrees" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
24
app/src/atlasWeather/res/layout/hourly_item_forecast.xml
Normal file
24
app/src/atlasWeather/res/layout/hourly_item_forecast.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/forecast_recyclerview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:spanCount="1"
|
||||
tools:itemCount="24"
|
||||
tools:listitem="@layout/widget_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -48,7 +48,6 @@ class MainViewModel(
|
||||
// Save data if not null
|
||||
repository.saveLastSavedAt(CURRENT_LOCATION)
|
||||
repository.saveCurrentWeatherToRoom(entityItem)
|
||||
onSuccess(Unit)
|
||||
} catch (e: Exception) {
|
||||
onError(e.message!!)
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class WorldViewModel(
|
||||
} else {
|
||||
repository.getSingleWeather(locationName)
|
||||
}
|
||||
onSuccess(Unit)
|
||||
repository.saveCurrentWeatherToRoom(weatherEntity)
|
||||
repository.saveLastSavedAt(weatherEntity.id)
|
||||
} catch (e: IOException) {
|
||||
|
||||
@@ -15,6 +15,5 @@ class ViewHolderForecast(
|
||||
val adapter = GridForecastAdapter()
|
||||
adapter.addCurrent(forecasts)
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
}
|
||||
}
|
||||
@@ -35,5 +35,5 @@ fun <T> LiveData<T>.getOrAwaitValue(
|
||||
}
|
||||
|
||||
fun sleep(millis: Long = 1000) {
|
||||
runBlocking(Dispatchers.IO) { delay(millis) }
|
||||
runBlocking(Dispatchers.Default) { delay(millis) }
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import com.appttude.h_mal.atlas_weather.utils.sleep
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.InjectMockKs
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@@ -28,6 +29,7 @@ class WorldViewModelTest : BaseTest() {
|
||||
@get:Rule
|
||||
val rule = InstantTaskExecutorRule()
|
||||
|
||||
@InjectMockKs
|
||||
lateinit var viewModel: WorldViewModel
|
||||
|
||||
@MockK(relaxed = true)
|
||||
@@ -36,12 +38,11 @@ class WorldViewModelTest : BaseTest() {
|
||||
@MockK
|
||||
lateinit var locationProvider: LocationProviderImpl
|
||||
|
||||
lateinit var weatherResponse: WeatherResponse
|
||||
private lateinit var weatherResponse: WeatherResponse
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
viewModel = WorldViewModel(locationProvider, repository)
|
||||
|
||||
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
|
||||
}
|
||||
@@ -49,18 +50,17 @@ class WorldViewModelTest : BaseTest() {
|
||||
@Test
|
||||
fun fetchDataForSingleLocation_validLocation_validReturn() {
|
||||
// Arrange
|
||||
val location = CURRENT_LOCATION
|
||||
val entityItem = EntityItem(CURRENT_LOCATION, FullWeather(weatherResponse).apply {
|
||||
temperatureUnit = "°C"
|
||||
locationString = CURRENT_LOCATION
|
||||
})
|
||||
|
||||
// Act
|
||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns Pair(
|
||||
weatherResponse.lat,
|
||||
weatherResponse.lon
|
||||
)
|
||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||
coEvery {
|
||||
repository.getWeatherFromApi(
|
||||
weatherResponse.lat.toString(),
|
||||
@@ -77,10 +77,14 @@ class WorldViewModelTest : BaseTest() {
|
||||
every { repository.saveLastSavedAt(CURRENT_LOCATION) } returns Unit
|
||||
coEvery { repository.saveCurrentWeatherToRoom(entityItem) } returns Unit
|
||||
|
||||
viewModel.fetchDataForSingleLocation(location)
|
||||
viewModel.fetchDataForSingleLocation(CURRENT_LOCATION)
|
||||
|
||||
// Assert
|
||||
sleep(300)
|
||||
viewModel.uiState.observeForever {
|
||||
println(it.javaClass.name)
|
||||
}
|
||||
|
||||
sleep(3000)
|
||||
assertIs<ViewState.HasData<*>>(viewModel.uiState.getOrAwaitValue())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user