Lint fixes (#19)
- Code inspection - Redundant resources removed - Resources moved the corresponding flavours - Deprecated dependencies upgraded - lint changes - circleci updated to capture screenshot
@@ -14,17 +14,11 @@ commands:
|
|||||||
description: checkout repo and android dependencies
|
description: checkout repo and android dependencies
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- android/restore-gradle-cache
|
|
||||||
build_gradle:
|
|
||||||
description: Build the gradle
|
|
||||||
steps:
|
|
||||||
- android/restore-gradle-cache
|
|
||||||
- run:
|
- run:
|
||||||
name: Download Dependencies
|
name: Give gradle permissions
|
||||||
command: |
|
command: |
|
||||||
sudo chmod +x ./gradlew
|
sudo chmod +x ./gradlew
|
||||||
./gradlew androidDependencies
|
- android/restore-gradle-cache
|
||||||
- android/save-gradle-cache
|
|
||||||
run_tests:
|
run_tests:
|
||||||
description: run tests for flavour specified
|
description: run tests for flavour specified
|
||||||
parameters:
|
parameters:
|
||||||
@@ -33,11 +27,11 @@ commands:
|
|||||||
default: "AtlasWeather"
|
default: "AtlasWeather"
|
||||||
steps:
|
steps:
|
||||||
# The next step will run the unit tests
|
# The next step will run the unit tests
|
||||||
- build_gradle
|
|
||||||
- run:
|
- run:
|
||||||
name: Run non-instrumentation unit tests
|
name: Run non-instrumentation unit tests
|
||||||
command: |
|
command: |
|
||||||
./gradlew test<< parameters.flavour >>DebugUnitTest --continue
|
./gradlew test<< parameters.flavour >>DebugUnitTest
|
||||||
|
- android/save-gradle-cache
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: app/build/reports
|
path: app/build/reports
|
||||||
destination: reports
|
destination: reports
|
||||||
@@ -49,24 +43,27 @@ commands:
|
|||||||
flavour:
|
flavour:
|
||||||
type: string
|
type: string
|
||||||
default: "AtlasWeather"
|
default: "AtlasWeather"
|
||||||
|
package_suffix:
|
||||||
|
type: string
|
||||||
|
default: "atlas_weather"
|
||||||
steps:
|
steps:
|
||||||
- build_gradle
|
|
||||||
- android/start-emulator-and-run-tests:
|
- android/start-emulator-and-run-tests:
|
||||||
post-emulator-launch-assemble-command: ./gradlew assemble<< parameters.flavour >>DebugAndroidTest
|
post-emulator-launch-assemble-command: ./gradlew assemble<< parameters.flavour >>DebugAndroidTest
|
||||||
test-command: ./gradlew connected<< parameters.flavour >>DebugAndroidTest
|
test-command: ./gradlew connected<< parameters.flavour >>DebugAndroidTest --continue
|
||||||
system-image: system-images;android-26;google_apis;x86
|
system-image: system-images;android-26;google_apis;x86
|
||||||
pull-data: true
|
pull-data: true
|
||||||
pull-data-path: /storage/emulated/0/Android/data/
|
|
||||||
pull-data-target: ~/app-data
|
pull-data-target: ~/app-data
|
||||||
restore-gradle-cache-prefix: v1a
|
# store screenshots for failed ui tests
|
||||||
|
- when:
|
||||||
|
condition: on_fail
|
||||||
|
steps:
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/app-data
|
||||||
|
destination: screenshots
|
||||||
# store test reports
|
# store test reports
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: app/build/reports/androidTests/connected
|
path: app/build/reports/androidTests/connected
|
||||||
destination: reports
|
destination: reports
|
||||||
# store screenshots for failed ui tests
|
|
||||||
- store_artifacts:
|
|
||||||
path: ~/app-data
|
|
||||||
destination: screenshots
|
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: app/build/outputs/androidTest-results/connected
|
path: app/build/outputs/androidTest-results/connected
|
||||||
deploy_to_play_store:
|
deploy_to_play_store:
|
||||||
@@ -83,7 +80,6 @@ commands:
|
|||||||
name: Setup playstore key
|
name: Setup playstore key
|
||||||
command: |
|
command: |
|
||||||
echo "$GOOGLE_PLAY_KEY" > "google-play-key.json"
|
echo "$GOOGLE_PLAY_KEY" > "google-play-key.json"
|
||||||
- build_gradle
|
|
||||||
- run:
|
- run:
|
||||||
name: Run fastlane command to deploy to playstore
|
name: Run fastlane command to deploy to playstore
|
||||||
command: |
|
command: |
|
||||||
@@ -101,6 +97,9 @@ jobs:
|
|||||||
flavour:
|
flavour:
|
||||||
type: string
|
type: string
|
||||||
default: "AtlasWeather"
|
default: "AtlasWeather"
|
||||||
|
package_suffix:
|
||||||
|
type: string
|
||||||
|
default: "atlas_weather"
|
||||||
# These next lines define the Android machine image executor.
|
# These next lines define the Android machine image executor.
|
||||||
# See: https://circleci.com/docs/2.0/executor-types/
|
# See: https://circleci.com/docs/2.0/executor-types/
|
||||||
executor:
|
executor:
|
||||||
@@ -138,6 +137,7 @@ workflows:
|
|||||||
- build-and-test:
|
- build-and-test:
|
||||||
context: appttude
|
context: appttude
|
||||||
flavour: "MonoWeather"
|
flavour: "MonoWeather"
|
||||||
|
package_suffix: "monoWeather"
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
@@ -156,6 +156,7 @@ workflows:
|
|||||||
- build-and-test:
|
- build-and-test:
|
||||||
context: appttude
|
context: appttude
|
||||||
flavour: "AtlasWeather"
|
flavour: "AtlasWeather"
|
||||||
|
package_suffix: "atlas_weather"
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
|
|||||||
13
.idea/androidTestResultsUserPreferences.xml
generated
@@ -29,6 +29,19 @@
|
|||||||
</AndroidTestResultsTableState>
|
</AndroidTestResultsTableState>
|
||||||
</value>
|
</value>
|
||||||
</entry>
|
</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="-409920851">
|
<entry key="-409920851">
|
||||||
<value>
|
<value>
|
||||||
<AndroidTestResultsTableState>
|
<AndroidTestResultsTableState>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
android {
|
android {
|
||||||
sourceSets{
|
sourceSets {
|
||||||
test {
|
test {
|
||||||
resources.srcDirs += ['src/test/resources']
|
resources.srcDirs += ['src/test/resources']
|
||||||
}
|
}
|
||||||
@@ -96,32 +96,29 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'com.google.android.material:material:1.2.1'
|
implementation 'com.google.android.material:material:1.2.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
|
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
|
||||||
implementation "com.google.android.gms:play-services-location:18.0.0"
|
implementation "com.google.android.gms:play-services-location:21.0.1"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
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 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
|
||||||
implementation 'androidx.test.espresso:espresso-idling-resource:3.4.0'
|
|
||||||
implementation 'androidx.preference:preference:1.2.1'
|
implementation 'androidx.preference:preference:1.2.1'
|
||||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
|
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1'
|
||||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
|
||||||
/ * Unit testing * /
|
/ * Unit testing * /
|
||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test:rules:1.2.0'
|
|
||||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
androidTestImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
/ * Fragment Navigation * /
|
||||||
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
|
||||||
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
|
||||||
/ * android unit testing and espresso * /
|
/ * android unit testing and espresso * /
|
||||||
androidTestImplementation 'androidx.test:rules:1.4.1-alpha06'
|
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||||
androidTestImplementation "androidx.test:core:1.4.0"
|
androidTestImplementation "androidx.test:core:1.5.0"
|
||||||
|
|
||||||
/ * Android Espresso * /
|
/ * Android Espresso * /
|
||||||
def testJunitVersion = "1.1.5"
|
def testJunitVersion = "1.1.5"
|
||||||
@@ -149,13 +146,13 @@ dependencies {
|
|||||||
androidTestImplementation "io.mockk:mockk-android:$mockk_ver"
|
androidTestImplementation "io.mockk:mockk-android:$mockk_ver"
|
||||||
|
|
||||||
/ * Retrofit * /
|
/ * Retrofit * /
|
||||||
def retrofit_ver = "2.8.1"
|
def retrofit_ver = "2.9.0"
|
||||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_ver"
|
implementation "com.squareup.retrofit2:retrofit:$retrofit_ver"
|
||||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit_ver"
|
implementation "com.squareup.retrofit2:converter-gson:$retrofit_ver"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
|
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
|
||||||
|
|
||||||
/ * Shared prefs * /
|
/ * Shared prefs * /
|
||||||
def prefs_ver = "1.1.1"
|
def prefs_ver = "1.2.0"
|
||||||
implementation "androidx.preference:preference-ktx:$prefs_ver"
|
implementation "androidx.preference:preference-ktx:$prefs_ver"
|
||||||
|
|
||||||
/ *Kodein Dependency Injection * /
|
/ *Kodein Dependency Injection * /
|
||||||
@@ -164,10 +161,10 @@ dependencies {
|
|||||||
implementation "org.kodein.di:kodein-di-framework-android-x:$kodein_version"
|
implementation "org.kodein.di:kodein-di-framework-android-x:$kodein_version"
|
||||||
|
|
||||||
/ * Room database * /
|
/ * Room database * /
|
||||||
def room_version = "2.3.0-alpha03"
|
def room_version = "2.4.3"
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
|
|
||||||
/ * Picasso * /
|
/ * Picasso * /
|
||||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||||
@@ -182,7 +179,7 @@ dependencies {
|
|||||||
implementation "com.tomtom.online:sdk-maps:2.4807"
|
implementation "com.tomtom.online:sdk-maps:2.4807"
|
||||||
|
|
||||||
/ * coroutines support for firebase operations * /
|
/ * coroutines support for firebase operations * /
|
||||||
def coroutines_google_ver = "1.1.1"
|
def coroutines_google_ver = "1.6.4"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutines_google_ver"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutines_google_ver"
|
||||||
|
|
||||||
/ * Picasso * /
|
/ * Picasso * /
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("EmptyMethod")
|
||||||
|
|
||||||
package com.appttude.h_mal.atlas_weather
|
package com.appttude.h_mal.atlas_weather
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
@@ -23,6 +25,7 @@ import org.junit.Rule
|
|||||||
import tools.fastlane.screengrab.Screengrab
|
import tools.fastlane.screengrab.Screengrab
|
||||||
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy
|
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy
|
||||||
|
|
||||||
|
@Suppress("EmptyMethod")
|
||||||
open class BaseTest<A : Activity>(
|
open class BaseTest<A : Activity>(
|
||||||
private val activity: Class<A>,
|
private val activity: Class<A>,
|
||||||
private val intentBundle: Bundle? = null,
|
private val intentBundle: Bundle? = null,
|
||||||
@@ -47,7 +50,8 @@ open class BaseTest<A : Activity>(
|
|||||||
startIntent.replaceExtras(intentBundle)
|
startIntent.replaceExtras(intentBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
testApp = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
|
testApp =
|
||||||
|
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
|
||||||
runBlocking {
|
runBlocking {
|
||||||
beforeLaunch()
|
beforeLaunch()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
|
|||||||
import androidx.test.espresso.contrib.PickerActions
|
import androidx.test.espresso.contrib.PickerActions
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
|
import com.appttude.h_mal.atlas_weather.helpers.EspressoHelper.waitForView
|
||||||
import com.appttude.h_mal.atlas_weather.helpers.checkErrorMessage
|
import com.appttude.h_mal.atlas_weather.helpers.checkErrorMessage
|
||||||
import com.appttude.h_mal.atlas_weather.helpers.checkImage
|
import com.appttude.h_mal.atlas_weather.helpers.checkImage
|
||||||
import com.appttude.h_mal.atlas_weather.helpers.EspressoHelper.waitForView
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
import org.hamcrest.CoreMatchers.anything
|
import org.hamcrest.CoreMatchers.anything
|
||||||
import org.hamcrest.CoreMatchers.equalTo
|
import org.hamcrest.CoreMatchers.equalTo
|
||||||
@@ -138,7 +138,7 @@ open class BaseTestRobot {
|
|||||||
fun getStringFromResource(@StringRes resId: Int): String =
|
fun getStringFromResource(@StringRes resId: Int): String =
|
||||||
Resources.getSystem().getString(resId)
|
Resources.getSystem().getString(resId)
|
||||||
|
|
||||||
fun pullToRefresh(resId: Int){
|
fun pullToRefresh(resId: Int) {
|
||||||
onView(allOf(withId(resId), isDisplayed())).perform(swipeDown())
|
onView(allOf(withId(resId), isDisplayed())).perform(swipeDown())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class TestAppClass : BaseAppClass() {
|
|||||||
|
|
||||||
override fun createNetworkModule(): WeatherApi {
|
override fun createNetworkModule(): WeatherApi {
|
||||||
return NetworkModule().invoke<WeatherApi>(
|
return NetworkModule().invoke<WeatherApi>(
|
||||||
mockingNetworkInterceptor,
|
mockingNetworkInterceptor,
|
||||||
NetworkConnectionInterceptor(this),
|
NetworkConnectionInterceptor(this),
|
||||||
QueryParamsInterceptor(),
|
QueryParamsInterceptor(),
|
||||||
loggingInterceptor
|
loggingInterceptor
|
||||||
) as WeatherApi
|
) as WeatherApi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,8 +36,8 @@ class TestAppClass : BaseAppClass() {
|
|||||||
|
|
||||||
override fun createRoomDatabase(): AppDatabase {
|
override fun createRoomDatabase(): AppDatabase {
|
||||||
return Room.inMemoryDatabaseBuilder(this, AppDatabase::class.java)
|
return Room.inMemoryDatabaseBuilder(this, AppDatabase::class.java)
|
||||||
.addTypeConverter(Converter(this))
|
.addTypeConverter(Converter(this))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stubUrl(url: String, rawPath: String) {
|
fun stubUrl(url: String, rawPath: String) {
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import androidx.test.runner.AndroidJUnitRunner
|
|||||||
|
|
||||||
class TestRunner : AndroidJUnitRunner() {
|
class TestRunner : AndroidJUnitRunner() {
|
||||||
@Throws(
|
@Throws(
|
||||||
InstantiationException::class,
|
InstantiationException::class,
|
||||||
IllegalAccessException::class,
|
IllegalAccessException::class,
|
||||||
ClassNotFoundException::class
|
ClassNotFoundException::class
|
||||||
)
|
)
|
||||||
override fun newApplication(
|
override fun newApplication(
|
||||||
cl: ClassLoader?,
|
cl: ClassLoader?,
|
||||||
className: String?,
|
className: String?,
|
||||||
context: Context?
|
context: Context?
|
||||||
): Application {
|
): Application {
|
||||||
return super.newApplication(cl, TestAppClass::class.java.name, context)
|
return super.newApplication(cl, TestAppClass::class.java.name, context)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ class MockLocationProvider : LocationProvider {
|
|||||||
override suspend fun getCurrentLatLong() = latLong
|
override suspend fun getCurrentLatLong() = latLong
|
||||||
override fun getLatLongFromLocationName(location: String) = latLong
|
override fun getLatLongFromLocationName(location: String) = latLong
|
||||||
|
|
||||||
override suspend fun getLocationNameFromLatLong(lat: Double, long: Double, type: LocationType): String {
|
override suspend fun getLocationNameFromLatLong(
|
||||||
|
lat: Double,
|
||||||
|
long: Double,
|
||||||
|
type: LocationType
|
||||||
|
): String {
|
||||||
return "Mock Location"
|
return "Mock Location"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import okhttp3.Response
|
|||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
|
|
||||||
class MockingNetworkInterceptor(
|
class MockingNetworkInterceptor(
|
||||||
private val idlingResource: CountingIdlingResource
|
private val idlingResource: CountingIdlingResource
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
private var feedMap: MutableMap<String, String> = mutableMapOf()
|
private var feedMap: MutableMap<String, String> = mutableMapOf()
|
||||||
@@ -27,11 +27,11 @@ class MockingNetworkInterceptor(
|
|||||||
val body = jsonPath.toResponseBody("application/json".toMediaType())
|
val body = jsonPath.toResponseBody("application/json".toMediaType())
|
||||||
|
|
||||||
val chainResponseBuilder = Response.Builder()
|
val chainResponseBuilder = Response.Builder()
|
||||||
.code(200)
|
.code(200)
|
||||||
.protocol(Protocol.HTTP_1_1)
|
.protocol(Protocol.HTTP_1_1)
|
||||||
.request(original)
|
.request(original)
|
||||||
.message("OK")
|
.message("OK")
|
||||||
.body(body)
|
.body(body)
|
||||||
idlingResource.decrement()
|
idlingResource.decrement()
|
||||||
return chainResponseBuilder.build()
|
return chainResponseBuilder.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.view.View
|
|||||||
import org.hamcrest.BaseMatcher
|
import org.hamcrest.BaseMatcher
|
||||||
import org.hamcrest.Description
|
import org.hamcrest.Description
|
||||||
|
|
||||||
class BaseMatcher: BaseMatcher<View>() {
|
class BaseMatcher : BaseMatcher<View>() {
|
||||||
override fun describeTo(description: Description?) {
|
override fun describeTo(description: Description?) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import androidx.test.espresso.ViewAction
|
|||||||
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
|
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
|
|
||||||
open class BaseViewAction: ViewAction {
|
@Suppress("EmptyMethod")
|
||||||
|
open class BaseViewAction : ViewAction {
|
||||||
override fun getDescription(): String? = setDescription()
|
override fun getDescription(): String? = setDescription()
|
||||||
|
|
||||||
override fun getConstraints(): Matcher<View> = setConstraints()
|
override fun getConstraints(): Matcher<View> = setConstraints()
|
||||||
@@ -15,13 +16,11 @@ open class BaseViewAction: ViewAction {
|
|||||||
setPerform(uiController, view)
|
setPerform(uiController, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun setDescription(): String? {
|
open fun setDescription(): String? = ""
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun setConstraints(): Matcher<View> {
|
open fun setConstraints(): Matcher<View> {
|
||||||
return isAssignableFrom(View::class.java)
|
return isAssignableFrom(View::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun setPerform(uiController: UiController?, view: View?) { }
|
open fun setPerform(uiController: UiController?, view: View?) {}
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ object EspressoHelper {
|
|||||||
var tries = 0
|
var tries = 0
|
||||||
val childViews: Iterable<View> = TreeIterables.breadthFirstViewTraversal(view)
|
val childViews: Iterable<View> = TreeIterables.breadthFirstViewTraversal(view)
|
||||||
|
|
||||||
// Look for the match in the tree of childviews
|
// Look for the match in the tree of child views
|
||||||
childViews.forEach {
|
childViews.forEach {
|
||||||
tries++
|
tries++
|
||||||
if (viewMatcher.matches(it)) {
|
if (viewMatcher.matches(it)) {
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ class SnapshotRule : TestWatcher() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getScreenshotName(description: Description): String {
|
fun getScreenshotName(description: Description): String {
|
||||||
return description.className.replace(".", "-") + "_" + description.methodName.replace(".", "-")
|
return description.className.replace(".", "-") + "_" + description.methodName.replace(
|
||||||
|
".",
|
||||||
|
"-"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package com.appttude.h_mal.atlas_weather.utils
|
|||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class DateUtilsKtTest{
|
class DateUtilsKtTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun toDayString_testStandardData_outputCorrect() {
|
fun toDayString_testStandardData_outputCorrect() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.utils
|
package com.appttude.h_mal.atlas_weather.utils
|
||||||
|
|
||||||
enum class Stubs(
|
enum class Stubs(
|
||||||
val id: String
|
val id: String
|
||||||
) {
|
) {
|
||||||
Valid("valid_response"),
|
Valid("valid_response"),
|
||||||
Invalid("invalid_response")
|
Invalid("invalid_response")
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import com.appttude.h_mal.atlas_weather.R
|
|||||||
|
|
||||||
fun homeScreen(func: HomeScreenRobot.() -> Unit) = HomeScreenRobot().apply { func() }
|
fun homeScreen(func: HomeScreenRobot.() -> Unit) = HomeScreenRobot().apply { func() }
|
||||||
class HomeScreenRobot : BaseTestRobot() {
|
class HomeScreenRobot : BaseTestRobot() {
|
||||||
fun verifyCurrentTemperature(temperature: Int) = matchText(R.id.temp_main_4, temperature.toString())
|
fun verifyCurrentTemperature(temperature: Int) =
|
||||||
|
matchText(R.id.temp_main_4, temperature.toString())
|
||||||
|
|
||||||
fun verifyCurrentLocation(location: String) = matchText(R.id.location_main_4, location)
|
fun verifyCurrentLocation(location: String) = matchText(R.id.location_main_4, location)
|
||||||
fun refresh() = pullToRefresh(R.id.swipe_refresh)
|
fun refresh() = pullToRefresh(R.id.swipe_refresh)
|
||||||
|
fun isDisplayed() = matchViewWaitFor(R.id.temp_main_4)
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
|||||||
@Test
|
@Test
|
||||||
fun loadApp_validWeatherResponse_returnsValidPage() {
|
fun loadApp_validWeatherResponse_returnsValidPage() {
|
||||||
homeScreen {
|
homeScreen {
|
||||||
waitFor(2000)
|
isDisplayed()
|
||||||
verifyCurrentTemperature(2)
|
verifyCurrentTemperature(2)
|
||||||
verifyCurrentLocation("Mock Location")
|
verifyCurrentLocation("Mock Location")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.test.espresso.matcher.ViewMatchers
|
|||||||
import com.appttude.h_mal.atlas_weather.BaseTest
|
import com.appttude.h_mal.atlas_weather.BaseTest
|
||||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
||||||
|
|
||||||
open class MonoBaseTest: BaseTest<MainActivity>(MainActivity::class.java) {
|
open class MonoBaseTest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||||
|
|
||||||
override fun afterLaunch() {
|
override fun afterLaunch() {
|
||||||
// Dismiss dialog on start up
|
// Dismiss dialog on start up
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package com.appttude.h_mal.monoWeather.robot
|
package com.appttude.h_mal.monoWeather.robot
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
|
||||||
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
||||||
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
|
||||||
fun homeScreen(func: HomeScreenRobot.() -> Unit) = HomeScreenRobot().apply { func() }
|
fun homeScreen(func: HomeScreenRobot.() -> Unit) = HomeScreenRobot().apply { func() }
|
||||||
class HomeScreenRobot : BaseTestRobot() {
|
class HomeScreenRobot : BaseTestRobot() {
|
||||||
fun verifyCurrentTemperature(temperature: Int) = matchText(R.id.temp_main_4, temperature.toString())
|
fun verifyCurrentTemperature(temperature: Int) =
|
||||||
|
matchText(R.id.temp_main_4, temperature.toString())
|
||||||
|
|
||||||
fun verifyCurrentLocation(location: String) = matchText(R.id.location_main_4, location)
|
fun verifyCurrentLocation(location: String) = matchText(R.id.location_main_4, location)
|
||||||
fun refresh() = pullToRefresh(R.id.swipe_refresh)
|
fun refresh() = pullToRefresh(R.id.swipe_refresh)
|
||||||
|
fun isDisplayed() = matchViewWaitFor(R.id.temp_main_4)
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.appttude.h_mal.monoWeather.robot
|
package com.appttude.h_mal.monoWeather.robot
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
|
||||||
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
||||||
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
|
||||||
fun widgetPermissionScreen(func: WidgetPermissionScreenRobot.() -> Unit) =
|
fun widgetPermissionScreen(func: WidgetPermissionScreenRobot.() -> Unit) =
|
||||||
WidgetPermissionScreenRobot().apply { func() }
|
WidgetPermissionScreenRobot().apply { func() }
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.appttude.h_mal.monoWeather.tests
|
package com.appttude.h_mal.monoWeather.tests
|
||||||
|
|
||||||
|
|
||||||
import com.appttude.h_mal.monoWeather.robot.homeScreen
|
|
||||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||||
import com.appttude.h_mal.monoWeather.MonoBaseTest
|
import com.appttude.h_mal.monoWeather.MonoBaseTest
|
||||||
|
import com.appttude.h_mal.monoWeather.robot.homeScreen
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class HomePageUITest : MonoBaseTest() {
|
class HomePageUITest : MonoBaseTest() {
|
||||||
@@ -15,6 +15,7 @@ class HomePageUITest : MonoBaseTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun loadApp_validWeatherResponse_returnsValidPage() {
|
fun loadApp_validWeatherResponse_returnsValidPage() {
|
||||||
homeScreen {
|
homeScreen {
|
||||||
|
isDisplayed()
|
||||||
verifyCurrentTemperature(2)
|
verifyCurrentTemperature(2)
|
||||||
verifyCurrentLocation("Mock Location")
|
verifyCurrentLocation("Mock Location")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.appttude.h_mal.monoWeather.tests
|
package com.appttude.h_mal.monoWeather.tests
|
||||||
|
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.test.rule.ActivityTestRule
|
|
||||||
import com.appttude.h_mal.atlas_weather.BaseTest
|
import com.appttude.h_mal.atlas_weather.BaseTest
|
||||||
import com.appttude.h_mal.monoWeather.robot.widgetPermissionScreen
|
import com.appttude.h_mal.monoWeather.robot.widgetPermissionScreen
|
||||||
import com.appttude.h_mal.monoWeather.ui.widget.WidgetLocationPermissionActivity
|
import com.appttude.h_mal.monoWeather.ui.widget.WidgetLocationPermissionActivity
|
||||||
|
|||||||
@@ -12,11 +12,12 @@
|
|||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:node="merge">
|
tools:node="merge">
|
||||||
|
|
||||||
<activity android:name=".ui.MainActivity"
|
<activity
|
||||||
|
android:name=".ui.MainActivity"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.NoActionBar">
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -24,12 +25,14 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".notification.NotificationReceiver"
|
android:name=".notification.NotificationReceiver"
|
||||||
android:parentActivityName=".MainActivity"
|
android:exported="true"
|
||||||
android:exported="true"/>
|
android:parentActivityName=".MainActivity" />
|
||||||
|
|
||||||
<receiver android:name=".widget.NewAppWidget"
|
<receiver
|
||||||
|
android:name=".widget.NewAppWidget"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package com.appttude.h_mal.atlas_weather.notification
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
|
||||||
data class NotificationData(
|
data class NotificationData(
|
||||||
val temp: String,
|
val temp: String,
|
||||||
val icon: Bitmap
|
val icon: Bitmap
|
||||||
)
|
)
|
||||||
@@ -34,7 +34,11 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
||||||
|
|
||||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
if (ActivityCompat.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
context.displayToast("Please enable location permissions")
|
context.displayToast("Please enable location permissions")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -52,22 +56,17 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
|
|
||||||
val pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
val pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|
||||||
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
val builder = Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
|
||||||
Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
|
|
||||||
} else {
|
|
||||||
Notification.Builder(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
val notification = builder.setContentTitle("Weather App")
|
val notification = builder.setContentTitle("Weather App")
|
||||||
.setContentText(weather.current?.main + "°C")
|
.setContentText(weather.current?.main + "°C")
|
||||||
.setSmallIcon(R.mipmap.ic_notif) //change icon
|
.setSmallIcon(R.mipmap.ic_notif) //change icon
|
||||||
// .setLargeIcon(Icon.createWithResource(context, getImageResource(forecastItem.getCurrentForecast().getIconURL(), context)))
|
// .setLargeIcon(Icon.createWithResource(context, getImageResource(forecastItem.getCurrentForecast().getIconURL(), context)))
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setContentIntent(pendingIntent).build()
|
.setContentIntent(pendingIntent).build()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
builder.setChannelId(NOTIFICATION_CHANNEL_ID)
|
||||||
builder.setChannelId(NOTIFICATION_CHANNEL_ID)
|
val notificationManager =
|
||||||
}
|
context!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
val notificationManager = context!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
notificationManager.notify(0, notification)
|
notificationManager.notify(0, notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ package com.appttude.h_mal.atlas_weather.ui
|
|||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity(){
|
abstract class BaseActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,10 @@ class WorldItemFragment : Fragment() {
|
|||||||
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).weatherDisplay
|
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).weatherDisplay
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
override fun onCreateView(
|
||||||
savedInstanceState: Bundle?): View? {
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
return inflater.inflate(R.layout.fragment_home, container, false)
|
||||||
}
|
}
|
||||||
@@ -32,7 +34,7 @@ class WorldItemFragment : Fragment() {
|
|||||||
|
|
||||||
val recyclerAdapter = WeatherRecyclerAdapter {
|
val recyclerAdapter = WeatherRecyclerAdapter {
|
||||||
val directions =
|
val directions =
|
||||||
WorldItemFragmentDirections.actionWorldItemFragmentToFurtherDetailsFragment(it)
|
WorldItemFragmentDirections.actionWorldItemFragmentToFurtherDetailsFragment(it)
|
||||||
navigateTo(directions)
|
navigateTo(directions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import kotlinx.android.synthetic.main.activity_further_info.*
|
|||||||
|
|
||||||
|
|
||||||
private const val WEATHER = "param1"
|
private const val WEATHER = "param1"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple [Fragment] subclass.
|
* A simple [Fragment] subclass.
|
||||||
* Use the [FurtherInfoFragment.newInstance] factory method to
|
* Use the [FurtherInfoFragment.newInstance] factory method to
|
||||||
@@ -24,7 +25,11 @@ class FurtherInfoFragment : Fragment() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
return inflater.inflate(R.layout.activity_further_info, container, false)
|
return inflater.inflate(R.layout.activity_further_info, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
interface DeclarationBuilder{
|
interface DeclarationBuilder {
|
||||||
val link: String
|
val link: String
|
||||||
val message: String
|
val message: String
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
class PermissionsDeclarationDialog(context: Context) : BaseDeclarationDialog(context) {
|
class PermissionsDeclarationDialog(context: Context) : BaseDeclarationDialog(context) {
|
||||||
|
|
||||||
override val link: String = "https://sites.google.com/view/hmaldev/home/monochrome"
|
override val link: String = "https://sites.google.com/view/hmaldev/home/monochrome"
|
||||||
override val message: String = "Hi, thank you for downloading my app. Google play isn't letting me upload my app to the Playstore until I have a privacy declaration :(. My app is basically used to demonstrate my code=ing to potential employers and others. I do NOT store or process any information. The location permission in the app is there just to provide the end user with weather data."
|
override val message: String =
|
||||||
|
"Hi, thank you for downloading my app. Google play isn't letting me upload my app to the Playstore until I have a privacy declaration :(. My app is basically used to demonstrate my code=ing to potential employers and others. I do NOT store or process any information. The location permission in the app is there just to provide the end user with weather data."
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class BaseDeclarationDialog(val context: Context): DeclarationBuilder {
|
abstract class BaseDeclarationDialog(val context: Context) : DeclarationBuilder {
|
||||||
abstract override val link: String
|
abstract override val link: String
|
||||||
abstract override val message: String
|
abstract override val message: String
|
||||||
|
|
||||||
@@ -23,14 +24,14 @@ abstract class BaseDeclarationDialog(val context: Context): DeclarationBuilder {
|
|||||||
val myMessage = buildMessage()
|
val myMessage = buildMessage()
|
||||||
|
|
||||||
val builder = AlertDialog.Builder(context)
|
val builder = AlertDialog.Builder(context)
|
||||||
.setPositiveButton("agree") { _, _ ->
|
.setPositiveButton("agree") { _, _ ->
|
||||||
agreeCallback()
|
agreeCallback()
|
||||||
}
|
}
|
||||||
.setNegativeButton("disagree") { _, _ ->
|
.setNegativeButton("disagree") { _, _ ->
|
||||||
disagreeCallback()
|
disagreeCallback()
|
||||||
}
|
}
|
||||||
.setMessage(myMessage)
|
.setMessage(myMessage)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
|
|
||||||
dialog = builder.create()
|
dialog = builder.create()
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
|
class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||||
@@ -16,7 +16,7 @@ class ViewHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemVi
|
|||||||
var avgTempTV: TextView = listItemView.findViewById(R.id.temp_main_4)
|
var avgTempTV: TextView = listItemView.findViewById(R.id.temp_main_4)
|
||||||
var tempUnit: TextView = listItemView.findViewById(R.id.temp_unit_4)
|
var tempUnit: TextView = listItemView.findViewById(R.id.temp_unit_4)
|
||||||
|
|
||||||
fun bindData(weather: WeatherDisplay?){
|
fun bindData(weather: WeatherDisplay?) {
|
||||||
locationTV.text = weather?.displayName
|
locationTV.text = weather?.displayName
|
||||||
conditionTV.text = weather?.description
|
conditionTV.text = weather?.description
|
||||||
weatherIV.loadImage(weather?.iconURL)
|
weatherIV.loadImage(weather?.iconURL)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ internal class ViewHolderFurtherDetails(itemView: View) : RecyclerView.ViewHolde
|
|||||||
var humidity: TextView = itemView.findViewById(R.id.humidity_)
|
var humidity: TextView = itemView.findViewById(R.id.humidity_)
|
||||||
var clouds: TextView = itemView.findViewById(R.id.clouds_)
|
var clouds: TextView = itemView.findViewById(R.id.clouds_)
|
||||||
|
|
||||||
fun bindData(weather: WeatherDisplay?){
|
fun bindData(weather: WeatherDisplay?) {
|
||||||
windSpeed.text = weather?.windSpeed
|
windSpeed.text = weather?.windSpeed
|
||||||
windDirection.text = weather?.windDirection
|
windDirection.text = weather?.windDirection
|
||||||
precipitation.text = weather?.precipitation
|
precipitation.text = weather?.precipitation
|
||||||
|
|||||||
@@ -10,30 +10,33 @@ import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
|||||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||||
|
|
||||||
class WeatherRecyclerAdapter(
|
class WeatherRecyclerAdapter(
|
||||||
val itemClick: (Forecast) -> Unit
|
val itemClick: (Forecast) -> Unit
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
var weather: WeatherDisplay? = null
|
var weather: WeatherDisplay? = null
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
fun addCurrent(current: WeatherDisplay){
|
fun addCurrent(current: WeatherDisplay) {
|
||||||
weather = current
|
weather = current
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (getDataType(viewType)){
|
return when (getDataType(viewType)) {
|
||||||
is ViewType.Empty -> {
|
is ViewType.Empty -> {
|
||||||
val emptyViewHolder = View(parent.context)
|
val emptyViewHolder = View(parent.context)
|
||||||
EmptyViewHolder(emptyViewHolder)
|
EmptyViewHolder(emptyViewHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Current -> {
|
is ViewType.Current -> {
|
||||||
val viewCurrent = parent.generateView(R.layout.list_item_current)
|
val viewCurrent = parent.generateView(R.layout.list_item_current)
|
||||||
ViewHolderCurrent(viewCurrent)
|
ViewHolderCurrent(viewCurrent)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Forecast -> {
|
is ViewType.Forecast -> {
|
||||||
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
||||||
ViewHolderForecast(viewForecast)
|
ViewHolderForecast(viewForecast)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Further -> {
|
is ViewType.Further -> {
|
||||||
val viewFurther = parent.generateView(R.layout.list_item_further)
|
val viewFurther = parent.generateView(R.layout.list_item_further)
|
||||||
ViewHolderFurtherDetails(viewFurther)
|
ViewHolderFurtherDetails(viewFurther)
|
||||||
@@ -41,7 +44,7 @@ class WeatherRecyclerAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ViewType{
|
sealed class ViewType {
|
||||||
object Empty : ViewType()
|
object Empty : ViewType()
|
||||||
object Current : ViewType()
|
object Current : ViewType()
|
||||||
object Forecast : ViewType()
|
object Forecast : ViewType()
|
||||||
@@ -49,7 +52,7 @@ class WeatherRecyclerAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getDataType(type: Int): ViewType {
|
private fun getDataType(type: Int): ViewType {
|
||||||
return when (type){
|
return when (type) {
|
||||||
0 -> ViewType.Empty
|
0 -> ViewType.Empty
|
||||||
1 -> ViewType.Current
|
1 -> ViewType.Current
|
||||||
2 -> ViewType.Forecast
|
2 -> ViewType.Forecast
|
||||||
@@ -61,23 +64,25 @@ class WeatherRecyclerAdapter(
|
|||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
if (weather == null) return 0
|
if (weather == null) return 0
|
||||||
|
|
||||||
return when(position){
|
return when (position) {
|
||||||
0 -> 1
|
0 -> 1
|
||||||
in 1 until itemCount -2 -> 2
|
in 1 until itemCount - 2 -> 2
|
||||||
itemCount - 1 -> 3
|
itemCount - 1 -> 3
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (getDataType(getItemViewType(position))){
|
when (getDataType(getItemViewType(position))) {
|
||||||
is ViewType.Empty -> {
|
is ViewType.Empty -> {
|
||||||
holder as EmptyViewHolder
|
holder as EmptyViewHolder
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Current -> {
|
is ViewType.Current -> {
|
||||||
val viewHolderCurrent = holder as ViewHolderCurrent
|
val viewHolderCurrent = holder as ViewHolderCurrent
|
||||||
viewHolderCurrent.bindData(weather)
|
viewHolderCurrent.bindData(weather)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Forecast -> {
|
is ViewType.Forecast -> {
|
||||||
val viewHolderForecast = holder as ViewHolderForecast
|
val viewHolderForecast = holder as ViewHolderForecast
|
||||||
|
|
||||||
@@ -88,6 +93,7 @@ class WeatherRecyclerAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Further -> {
|
is ViewType.Further -> {
|
||||||
val viewHolderCurrent = holder as ViewHolderFurtherDetails
|
val viewHolderCurrent = holder as ViewHolderFurtherDetails
|
||||||
viewHolderCurrent.bindData(weather)
|
viewHolderCurrent.bindData(weather)
|
||||||
@@ -98,7 +104,7 @@ class WeatherRecyclerAdapter(
|
|||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
if (weather == null) return 0
|
if (weather == null) return 0
|
||||||
return 2 + (weather?.forecast?.size?: 0)
|
return 2 + (weather?.forecast?.size ?: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -34,11 +34,16 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||||||
setupNotificationBroadcaster(requireContext())
|
setupNotificationBroadcaster(requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == "widget_black_background"){
|
if (key == "widget_black_background") {
|
||||||
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
|
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
|
||||||
val widgetManager = AppWidgetManager.getInstance(requireContext())
|
val widgetManager = AppWidgetManager.getInstance(requireContext())
|
||||||
val ids =
|
val ids =
|
||||||
widgetManager.getAppWidgetIds(ComponentName(requireContext(), NewAppWidget::class.java))
|
widgetManager.getAppWidgetIds(
|
||||||
|
ComponentName(
|
||||||
|
requireContext(),
|
||||||
|
NewAppWidget::class.java
|
||||||
|
)
|
||||||
|
)
|
||||||
AppWidgetManager.getInstance(requireContext())
|
AppWidgetManager.getInstance(requireContext())
|
||||||
.notifyAppWidgetViewDataChanged(ids, R.id.whole_widget_view)
|
.notifyAppWidgetViewDataChanged(ids, R.id.whole_widget_view)
|
||||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
||||||
@@ -50,12 +55,19 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||||||
fun setupNotificationBroadcaster(context: Context) {
|
fun setupNotificationBroadcaster(context: Context) {
|
||||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
val notificationIntent = Intent(context, NotificationReceiver::class.java)
|
val notificationIntent = Intent(context, NotificationReceiver::class.java)
|
||||||
val broadcast = PendingIntent.getBroadcast(context, 100, notificationIntent,
|
val broadcast = PendingIntent.getBroadcast(
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
context, 100, notificationIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
val cal: Calendar = Calendar.getInstance()
|
val cal: Calendar = Calendar.getInstance()
|
||||||
cal.set(Calendar.HOUR_OF_DAY, 6)
|
cal.set(Calendar.HOUR_OF_DAY, 6)
|
||||||
cal.set(Calendar.MINUTE, 8)
|
cal.set(Calendar.MINUTE, 8)
|
||||||
cal.set(Calendar.SECOND, 5)
|
cal.set(Calendar.SECOND, 5)
|
||||||
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.timeInMillis, AlarmManager.INTERVAL_DAY, broadcast)
|
alarmManager.setRepeating(
|
||||||
|
AlarmManager.RTC_WAKEUP,
|
||||||
|
cal.timeInMillis,
|
||||||
|
AlarmManager.INTERVAL_DAY,
|
||||||
|
broadcast
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ class AddLocationFragment : BaseFragment(R.layout.activity_add_forecast) {
|
|||||||
|
|
||||||
submit.setOnClickListener {
|
submit.setOnClickListener {
|
||||||
val locationName = location_name_tv.text?.trim()?.toString()
|
val locationName = location_name_tv.text?.trim()?.toString()
|
||||||
if (locationName.isNullOrBlank()){
|
if (locationName.isNullOrBlank()) {
|
||||||
submit.error = "Location cannot be blank"
|
submit.error = "Location cannot be blank"
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ class AddLocationFragment : BaseFragment(R.layout.activity_add_forecast) {
|
|||||||
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
|
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
|
||||||
|
|
||||||
viewModel.operationComplete.observe(viewLifecycleOwner) {
|
viewModel.operationComplete.observe(viewLifecycleOwner) {
|
||||||
it?.getContentIfNotHandled()?.let {message ->
|
it?.getContentIfNotHandled()?.let { message ->
|
||||||
displayToast(message)
|
displayToast(message)
|
||||||
}
|
}
|
||||||
goBack()
|
goBack()
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.ui.world
|
package com.appttude.h_mal.atlas_weather.ui.world
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
import com.appttude.h_mal.atlas_weather.utils.navigateTo
|
import com.appttude.h_mal.atlas_weather.utils.navigateTo
|
||||||
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
|
||||||
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
||||||
import com.appttude.h_mal.monoWeather.ui.BaseFragment
|
import com.appttude.h_mal.monoWeather.ui.BaseFragment
|
||||||
import kotlinx.android.synthetic.main.fragment_add_location.*
|
import kotlinx.android.synthetic.atlasWeather.fragment_add_location.floatingActionButton
|
||||||
import org.kodein.di.KodeinAware
|
import kotlinx.android.synthetic.atlasWeather.fragment_add_location.progressBar2
|
||||||
import org.kodein.di.android.x.kodein
|
import kotlinx.android.synthetic.atlasWeather.fragment_add_location.world_recycler
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,9 +24,9 @@ class WorldFragment : BaseFragment(R.layout.fragment_add_location) {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val recyclerAdapter = WorldRecyclerAdapter{
|
val recyclerAdapter = WorldRecyclerAdapter {
|
||||||
val direction =
|
val direction =
|
||||||
WorldFragmentDirections.actionWorldFragmentToWorldItemFragment(it)
|
WorldFragmentDirections.actionWorldFragmentToWorldItemFragment(it)
|
||||||
navigateTo(direction)
|
navigateTo(direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +39,7 @@ class WorldFragment : BaseFragment(R.layout.fragment_add_location) {
|
|||||||
recyclerAdapter.addCurrent(it)
|
recyclerAdapter.addCurrent(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
floatingActionButton.setOnClickListener{
|
floatingActionButton.setOnClickListener {
|
||||||
navigateTo(R.id.action_worldFragment_to_addLocationFragment)
|
navigateTo(R.id.action_worldFragment_to_addLocationFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,22 +11,23 @@ import com.appttude.h_mal.atlas_weather.utils.generateView
|
|||||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||||
|
|
||||||
class WorldRecyclerAdapter(
|
class WorldRecyclerAdapter(
|
||||||
val itemClick: (WeatherDisplay) -> Unit
|
val itemClick: (WeatherDisplay) -> Unit
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
var weather: MutableList<WeatherDisplay> = mutableListOf()
|
var weather: MutableList<WeatherDisplay> = mutableListOf()
|
||||||
|
|
||||||
fun addCurrent(current: List<WeatherDisplay>){
|
fun addCurrent(current: List<WeatherDisplay>) {
|
||||||
weather.clear()
|
weather.clear()
|
||||||
weather.addAll(current)
|
weather.addAll(current)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (getDataType(viewType)){
|
return when (getDataType(viewType)) {
|
||||||
is ViewType.Empty -> {
|
is ViewType.Empty -> {
|
||||||
val emptyViewHolder = View(parent.context)
|
val emptyViewHolder = View(parent.context)
|
||||||
EmptyViewHolder(emptyViewHolder)
|
EmptyViewHolder(emptyViewHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Current -> {
|
is ViewType.Current -> {
|
||||||
val viewCurrent = parent.generateView(R.layout.db_list_item)
|
val viewCurrent = parent.generateView(R.layout.db_list_item)
|
||||||
WorldHolderCurrent(viewCurrent)
|
WorldHolderCurrent(viewCurrent)
|
||||||
@@ -34,13 +35,13 @@ class WorldRecyclerAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ViewType{
|
sealed class ViewType {
|
||||||
object Empty : ViewType()
|
object Empty : ViewType()
|
||||||
object Current : ViewType()
|
object Current : ViewType()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDataType(type: Int): ViewType{
|
private fun getDataType(type: Int): ViewType {
|
||||||
return when (type){
|
return when (type) {
|
||||||
0 -> ViewType.Empty
|
0 -> ViewType.Empty
|
||||||
1 -> ViewType.Current
|
1 -> ViewType.Current
|
||||||
else -> ViewType.Empty
|
else -> ViewType.Empty
|
||||||
@@ -52,11 +53,12 @@ class WorldRecyclerAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (getDataType(getItemViewType(position))){
|
when (getDataType(getItemViewType(position))) {
|
||||||
is ViewType.Empty -> {
|
is ViewType.Empty -> {
|
||||||
holder as EmptyViewHolder
|
holder as EmptyViewHolder
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Current -> {
|
is ViewType.Current -> {
|
||||||
val viewHolderCurrent = holder as WorldHolderCurrent
|
val viewHolderCurrent = holder as WorldHolderCurrent
|
||||||
viewHolderCurrent.bindData(weather[position])
|
viewHolderCurrent.bindData(weather[position])
|
||||||
@@ -79,7 +81,7 @@ class WorldRecyclerAdapter(
|
|||||||
var avgTempTV: TextView = listItemView.findViewById(R.id.db_main_temp)
|
var avgTempTV: TextView = listItemView.findViewById(R.id.db_main_temp)
|
||||||
// var tempUnit: TextView = listItemView.findViewById(R.id.db_minor_temp)
|
// var tempUnit: TextView = listItemView.findViewById(R.id.db_minor_temp)
|
||||||
|
|
||||||
fun bindData(weather: WeatherDisplay?){
|
fun bindData(weather: WeatherDisplay?) {
|
||||||
locationTV.text = weather?.location
|
locationTV.text = weather?.location
|
||||||
conditionTV.text = weather?.description
|
conditionTV.text = weather?.description
|
||||||
weatherIV.loadImage(weather?.iconURL)
|
weatherIV.loadImage(weather?.iconURL)
|
||||||
@@ -90,7 +92,6 @@ class WorldRecyclerAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||||
internal class EmptyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<gradient
|
<gradient
|
||||||
android:startColor="@color/colour_two"
|
android:angle="45"
|
||||||
android:centerColor="@color/colour_three"
|
android:centerColor="@color/colour_three"
|
||||||
android:endColor="@color/colour_four"
|
android:endColor="@color/colour_four"
|
||||||
android:type="linear"
|
android:startColor="@color/colour_two"
|
||||||
android:angle="45"/>
|
android:type="linear" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|||||||
16
app/src/atlasWeather/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group
|
||||||
|
android:scaleX="2.61"
|
||||||
|
android:scaleY="2.61"
|
||||||
|
android:translateX="22.68"
|
||||||
|
android:translateY="22.68">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
55
app/src/atlasWeather/res/layout/activity_add_forecast.xml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_margin="24dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/location_name_tv"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:ems="10"
|
||||||
|
android:hint="@string/location_name"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
android:maxLines="2"
|
||||||
|
tools:text="Greater London" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/submit"
|
||||||
|
style="?android:attr/borderlessButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:text="@string/submit"
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
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>
|
||||||
@@ -3,11 +3,13 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -20,13 +22,15 @@
|
|||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:src="@drawable/somethingnew"
|
style="@style/icon_style__further_details"
|
||||||
style="@style/icon_style__further_deatils" />
|
android:src="@drawable/somethingnew" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -55,6 +59,7 @@
|
|||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
tools:text="11" />
|
tools:text="11" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@@ -72,6 +77,7 @@
|
|||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
tools:text="11" />
|
tools:text="11" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@@ -96,6 +102,7 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@@ -107,13 +114,14 @@
|
|||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:src="@drawable/breeze"
|
style="@style/icon_style__further_details"
|
||||||
style="@style/icon_style__further_deatils" />
|
android:src="@drawable/breeze" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_weight="2"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="2">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -137,12 +145,12 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="7mph"
|
android:text="7mph" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@@ -154,13 +162,15 @@
|
|||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:src="@drawable/water_drop"
|
style="@style/icon_style__further_details"
|
||||||
style="@style/icon_style__further_deatils" />
|
android:src="@drawable/water_drop" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_weight="2"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="2">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -187,27 +197,26 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="85%"
|
android:text="85%" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:layout_weight="2"
|
|
||||||
android:text="Precip: " />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/preciptext"
|
android:layout_width="0dp"
|
||||||
android:layout_width="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_weight="2"
|
||||||
android:layout_weight="3"
|
android:text="Precip: " />
|
||||||
android:text="11mm"
|
|
||||||
/>
|
<TextView
|
||||||
</LinearLayout>
|
android:id="@+id/preciptext"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:text="11mm" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -215,6 +224,7 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@@ -226,13 +236,15 @@
|
|||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:src="@drawable/sunrise"
|
style="@style/icon_style__further_details"
|
||||||
style="@style/icon_style__further_deatils" />
|
android:src="@drawable/sunrise" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_weight="2"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="2">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -245,22 +257,21 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
android:text="UV: " />
|
android:text="UV: " />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/uvtext"
|
android:id="@+id/uvtext"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="7"
|
android:text="7" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -278,9 +289,9 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="05:30am"
|
android:text="05:30am" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@@ -296,8 +307,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="06:12pm"
|
android:text="06:12pm" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/main_content"
|
android:id="@+id/main_content"
|
||||||
@@ -17,19 +16,19 @@
|
|||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:popupTheme="@style/AppTheme.PopupOverlay"
|
app:popupTheme="@style/AppTheme.PopupOverlay"
|
||||||
tools:title="Atlas Weather"/>
|
tools:title="Atlas Weather" />
|
||||||
|
|
||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
android:id="@+id/nav_view"
|
android:id="@+id/nav_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
|
app:backgroundTint="@android:color/transparent"
|
||||||
|
app:elevation="0dp"
|
||||||
app:itemIconTint="@android:color/background_light"
|
app:itemIconTint="@android:color/background_light"
|
||||||
app:itemTextColor="@android:color/background_light"
|
app:itemTextColor="@android:color/background_light"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:backgroundTint="@android:color/transparent"
|
|
||||||
app:elevation="0dp"
|
|
||||||
app:menu="@menu/tabs_menu" />
|
app:menu="@menu/tabs_menu" />
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
@@ -42,7 +41,7 @@
|
|||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||||
tools:layout="@layout/fragment_home"/>
|
tools:layout="@layout/fragment_home" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress_circular"
|
android:id="@+id/progress_circular"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -8,39 +7,39 @@
|
|||||||
tools:context="com.appttude.h_mal.atlas_weather.ui.world.AddLocationFragment">
|
tools:context="com.appttude.h_mal.atlas_weather.ui.world.AddLocationFragment">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/world_recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:divider="@null"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
android:id="@+id/world_recycler"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:divider="@null">
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/floatingActionButton"
|
android:id="@+id/floatingActionButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
android:layout_marginBottom="36dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="36dp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
app:srcCompat="@android:drawable/ic_input_add"
|
android:contentDescription="@string/image_string"
|
||||||
|
android:focusable="true"
|
||||||
android:tint="@android:color/white"
|
android:tint="@android:color/white"
|
||||||
app:elevation="0dp"
|
app:elevation="0dp"
|
||||||
android:focusable="true" />
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar2"
|
android:id="@+id/progressBar2"
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:visibility="gone"
|
tools:visibility="visible" />
|
||||||
tools:visibility="visible"/>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginRight="6dp"
|
android:layout_marginRight="6dp"
|
||||||
android:tint="@color/colorAccent"
|
android:src="@drawable/location_flag"
|
||||||
android:src="@drawable/location_flag" />
|
android:tint="@color/colorAccent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/location_main_4"
|
android:id="@+id/location_main_4"
|
||||||
@@ -58,9 +58,9 @@
|
|||||||
android:textSize="30sp" />
|
android:textSize="30sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_below="@id/location_holder"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/location_holder"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -84,8 +84,7 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginRight="15dp"
|
android:layout_marginRight="15dp"
|
||||||
android:layout_weight="6"
|
android:layout_weight="6"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/condition_main_4"
|
android:id="@+id/condition_main_4"
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:cardElevation="0dp"
|
android:layout_marginTop="2dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
android:backgroundTint="@android:color/transparent"
|
android:backgroundTint="@android:color/transparent"
|
||||||
|
app:cardElevation="0dp"
|
||||||
card_view:cardPreventCornerOverlap="false"
|
card_view:cardPreventCornerOverlap="false"
|
||||||
card_view:cardUseCompatPadding="true">
|
card_view:cardUseCompatPadding="true">
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:src="@drawable/breeze"
|
style="@style/icon_style__further_details"
|
||||||
style="@style/icon_style__further_deatils" />
|
android:src="@drawable/breeze" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
@@ -50,8 +50,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="11kmp"
|
android:text="11kmp" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -70,8 +69,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="SW"
|
android:text="SW" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
@@ -92,7 +90,7 @@
|
|||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
style="@style/icon_style__further_deatils"
|
style="@style/icon_style__further_details"
|
||||||
android:src="@drawable/water_drop" />
|
android:src="@drawable/water_drop" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
@@ -126,8 +124,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="85%"
|
android:text="85%" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -145,8 +142,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="11mm"
|
android:text="11mm" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
@@ -167,7 +163,7 @@
|
|||||||
android:layout_weight="2">
|
android:layout_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
style="@style/icon_style__further_deatils"
|
style="@style/icon_style__further_details"
|
||||||
android:src="@drawable/cloud_symbol" />
|
android:src="@drawable/cloud_symbol" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
@@ -202,8 +198,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3"
|
android:layout_weight="3"
|
||||||
android:text="85%"
|
android:text="85%" />
|
||||||
/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -76,11 +76,11 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/widget_current_location"
|
android:id="@+id/widget_current_location"
|
||||||
|
style="@style/widget_light_home_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:maxWidth="180dp"
|
android:maxWidth="180dp"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
style="@style/widget_light_home_text"
|
|
||||||
tools:text="Hammersmith Bridge" />
|
tools:text="Hammersmith Bridge" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 671 B After Width: | Height: | Size: 671 B |
|
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 881 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -32,7 +32,7 @@
|
|||||||
tools:layout="@layout/fragment__two">
|
tools:layout="@layout/fragment__two">
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_worldFragment_to_addLocationFragment"
|
android:id="@+id/action_worldFragment_to_addLocationFragment"
|
||||||
app:destination="@id/addLocationFragment"/>
|
app:destination="@id/addLocationFragment" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_worldFragment_to_worldItemFragment"
|
android:id="@+id/action_worldFragment_to_worldItemFragment"
|
||||||
app:destination="@id/worldItemFragment" />
|
app:destination="@id/worldItemFragment" />
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<!-- TODO: Remove or change this placeholder text -->
|
<string name="min">Min:</string>
|
||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="max">Max:</string>
|
||||||
|
<string name="average">Average:</string>
|
||||||
|
<string name="floating_action_button">Floating Action Button</string>
|
||||||
|
<string name="_11mm">11mm</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -3,13 +3,13 @@
|
|||||||
android:configure="com.appttude.h_mal.atlas_weather.ui.widget.WidgetLocationPermissionActivity"
|
android:configure="com.appttude.h_mal.atlas_weather.ui.widget.WidgetLocationPermissionActivity"
|
||||||
android:initialKeyguardLayout="@layout/weather_app_widget"
|
android:initialKeyguardLayout="@layout/weather_app_widget"
|
||||||
android:initialLayout="@layout/weather_app_widget"
|
android:initialLayout="@layout/weather_app_widget"
|
||||||
android:minHeight="110.0dp"
|
|
||||||
android:minWidth="320.0dp"
|
android:minWidth="320.0dp"
|
||||||
|
android:minHeight="110.0dp"
|
||||||
android:minResizeWidth="320.0dp"
|
android:minResizeWidth="320.0dp"
|
||||||
android:minResizeHeight="110.0dp"
|
android:minResizeHeight="110.0dp"
|
||||||
android:previewImage="@drawable/widget_screenshot"
|
android:previewImage="@drawable/widget_screenshot"
|
||||||
android:updatePeriodMillis="1800000"
|
|
||||||
android:resizeMode="vertical"
|
android:resizeMode="vertical"
|
||||||
|
android:updatePeriodMillis="1800000"
|
||||||
android:widgetCategory="home_screen">
|
android:widgetCategory="home_screen">
|
||||||
|
|
||||||
</appwidget-provider>
|
</appwidget-provider>
|
||||||
@@ -1,32 +1,32 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<!-- <PreferenceCategory android:title="Units">-->
|
<!-- <PreferenceCategory android:title="Units">-->
|
||||||
<!-- <ListPreference-->
|
<!-- <ListPreference-->
|
||||||
<!-- android:defaultValue="°C"-->
|
<!-- android:defaultValue="°C"-->
|
||||||
<!-- android:entries="@array/list_preference_temp"-->
|
<!-- android:entries="@array/list_preference_temp"-->
|
||||||
<!-- android:entryValues="@array/list_preference_temp"-->
|
<!-- android:entryValues="@array/list_preference_temp"-->
|
||||||
<!-- android:key="temp_units"-->
|
<!-- android:key="temp_units"-->
|
||||||
<!-- android:title="Temperature Units" />-->
|
<!-- android:title="Temperature Units" />-->
|
||||||
<!-- <ListPreference-->
|
<!-- <ListPreference-->
|
||||||
<!-- android:defaultValue="kph"-->
|
<!-- android:defaultValue="kph"-->
|
||||||
<!-- android:entries="@array/list_preference_wind"-->
|
<!-- android:entries="@array/list_preference_wind"-->
|
||||||
<!-- android:entryValues="@array/list_preference_wind_values"-->
|
<!-- android:entryValues="@array/list_preference_wind_values"-->
|
||||||
<!-- android:key="wind_units"-->
|
<!-- android:key="wind_units"-->
|
||||||
<!-- android:title="Wind Units" />-->
|
<!-- android:title="Wind Units" />-->
|
||||||
<!-- <ListPreference-->
|
<!-- <ListPreference-->
|
||||||
<!-- android:defaultValue="mm"-->
|
<!-- android:defaultValue="mm"-->
|
||||||
<!-- android:entries="@array/list_preference_precip"-->
|
<!-- android:entries="@array/list_preference_precip"-->
|
||||||
<!-- android:entryValues="@array/list_preference_precip_values"-->
|
<!-- android:entryValues="@array/list_preference_precip_values"-->
|
||||||
<!-- android:key="precip_units"-->
|
<!-- android:key="precip_units"-->
|
||||||
<!-- android:title="Precipitation Units" />-->
|
<!-- android:title="Precipitation Units" />-->
|
||||||
<!-- <ListPreference-->
|
<!-- <ListPreference-->
|
||||||
<!-- android:defaultValue="km"-->
|
<!-- android:defaultValue="km"-->
|
||||||
<!-- android:entries="@array/list_preference_vis"-->
|
<!-- android:entries="@array/list_preference_vis"-->
|
||||||
<!-- android:entryValues="@array/list_preference_vis_values"-->
|
<!-- android:entryValues="@array/list_preference_vis_values"-->
|
||||||
<!-- android:key="vis_units"-->
|
<!-- android:key="vis_units"-->
|
||||||
<!-- android:title="Visibility Units" />-->
|
<!-- android:title="Visibility Units" />-->
|
||||||
<!-- </PreferenceCategory>-->
|
<!-- </PreferenceCategory>-->
|
||||||
<PreferenceCategory android:title="Notification Settings">
|
<PreferenceCategory android:title="Notification Settings">
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
|
|
||||||
<application android:networkSecurityConfig="@xml/network_security_config" />
|
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.location"
|
android:name="android.hardware.location"
|
||||||
android:required="true" />
|
android:required="true" />
|
||||||
@@ -19,4 +17,8 @@
|
|||||||
android:name="android.hardware.location.gps"
|
android:name="android.hardware.location.gps"
|
||||||
android:required="true" />
|
android:required="true" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
tools:ignore="MissingApplicationIcon" />
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -14,9 +14,9 @@ class AppClass : BaseAppClass() {
|
|||||||
|
|
||||||
override fun createNetworkModule(): WeatherApi {
|
override fun createNetworkModule(): WeatherApi {
|
||||||
return NetworkModule().invoke<WeatherApi>(
|
return NetworkModule().invoke<WeatherApi>(
|
||||||
NetworkConnectionInterceptor(this),
|
NetworkConnectionInterceptor(this),
|
||||||
QueryParamsInterceptor(),
|
QueryParamsInterceptor(),
|
||||||
loggingInterceptor
|
loggingInterceptor
|
||||||
) as WeatherApi
|
) as WeatherApi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,21 +9,21 @@ import com.tomtom.online.sdk.search.data.common.Address
|
|||||||
import com.tomtom.online.sdk.search.data.reversegeocoder.ReverseGeocoderSearchQueryBuilder
|
import com.tomtom.online.sdk.search.data.reversegeocoder.ReverseGeocoderSearchQueryBuilder
|
||||||
|
|
||||||
abstract class LocationHelper(
|
abstract class LocationHelper(
|
||||||
context: Context
|
context: Context
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val key = BuildConfig.ParamTwo
|
private val key = BuildConfig.ParamTwo
|
||||||
private val searchApi = OnlineSearchApi.create(context, key)
|
private val searchApi = OnlineSearchApi.create(context, key)
|
||||||
|
|
||||||
suspend fun getAddressFromLatLong(
|
suspend fun getAddressFromLatLong(
|
||||||
lat: Double, long: Double
|
lat: Double, long: Double
|
||||||
): Address? {
|
): Address? {
|
||||||
return createSuspend {
|
return createSuspend {
|
||||||
val revGeoQuery =
|
val revGeoQuery =
|
||||||
ReverseGeocoderSearchQueryBuilder(lat, long).build()
|
ReverseGeocoderSearchQueryBuilder(lat, long).build()
|
||||||
|
|
||||||
val resultSingle =
|
val resultSingle =
|
||||||
searchApi.reverseGeocoding(revGeoQuery)
|
searchApi.reverseGeocoding(revGeoQuery)
|
||||||
|
|
||||||
resultSingle.blockingGet()?.addresses?.get(0)?.address
|
resultSingle.blockingGet()?.addresses?.get(0)?.address
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ interface LocationProvider {
|
|||||||
suspend fun getCurrentLatLong(): Pair<Double, Double>
|
suspend fun getCurrentLatLong(): Pair<Double, Double>
|
||||||
fun getLatLongFromLocationName(location: String): Pair<Double, Double>
|
fun getLatLongFromLocationName(location: String): Pair<Double, Double>
|
||||||
suspend fun getLocationNameFromLatLong(
|
suspend fun getLocationNameFromLatLong(
|
||||||
lat: Double,
|
lat: Double,
|
||||||
long: Double,
|
long: Double,
|
||||||
type: LocationType = LocationType.Town
|
type: LocationType = LocationType.Town
|
||||||
): String
|
): String
|
||||||
}
|
}
|
||||||
@@ -9,12 +9,11 @@ import android.location.LocationManager
|
|||||||
import android.os.HandlerThread
|
import android.os.HandlerThread
|
||||||
import androidx.annotation.RequiresPermission
|
import androidx.annotation.RequiresPermission
|
||||||
import com.appttude.h_mal.atlas_weather.model.types.LocationType
|
import com.appttude.h_mal.atlas_weather.model.types.LocationType
|
||||||
import com.google.android.gms.location.FusedLocationProviderClient
|
|
||||||
import com.google.android.gms.location.LocationCallback
|
import com.google.android.gms.location.LocationCallback
|
||||||
import com.google.android.gms.location.LocationRequest
|
import com.google.android.gms.location.LocationRequest
|
||||||
import com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY
|
|
||||||
import com.google.android.gms.location.LocationRequest.PRIORITY_LOW_POWER
|
|
||||||
import com.google.android.gms.location.LocationResult
|
import com.google.android.gms.location.LocationResult
|
||||||
|
import com.google.android.gms.location.LocationServices
|
||||||
|
import com.google.android.gms.location.Priority
|
||||||
import com.google.android.gms.tasks.CancellationToken
|
import com.google.android.gms.tasks.CancellationToken
|
||||||
import com.google.android.gms.tasks.OnTokenCanceledListener
|
import com.google.android.gms.tasks.OnTokenCanceledListener
|
||||||
import kotlinx.coroutines.tasks.await
|
import kotlinx.coroutines.tasks.await
|
||||||
@@ -28,8 +27,8 @@ class LocationProviderImpl(
|
|||||||
private val applicationContext: Context
|
private val applicationContext: Context
|
||||||
) : LocationProvider, LocationHelper(applicationContext) {
|
) : LocationProvider, LocationHelper(applicationContext) {
|
||||||
private var locationManager =
|
private var locationManager =
|
||||||
applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
|
applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
|
||||||
private val client = FusedLocationProviderClient(applicationContext)
|
private val client = LocationServices.getFusedLocationProviderClient(applicationContext)
|
||||||
private val geoCoder: Geocoder by lazy { Geocoder(applicationContext, Locale.getDefault()) }
|
private val geoCoder: Geocoder by lazy { Geocoder(applicationContext, Locale.getDefault()) }
|
||||||
|
|
||||||
@RequiresPermission(value = ACCESS_COARSE_LOCATION)
|
@RequiresPermission(value = ACCESS_COARSE_LOCATION)
|
||||||
@@ -48,18 +47,19 @@ class LocationProviderImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getLocationNameFromLatLong(
|
override suspend fun getLocationNameFromLatLong(
|
||||||
lat: Double, long: Double, type: LocationType
|
lat: Double, long: Double, type: LocationType
|
||||||
): String {
|
): String {
|
||||||
val address = getAddressFromLatLong(lat, long) ?: return "$lat $long"
|
val address = getAddressFromLatLong(lat, long) ?: return "$lat $long"
|
||||||
|
|
||||||
return when (type) {
|
return when (type) {
|
||||||
LocationType.Town -> {
|
LocationType.Town -> {
|
||||||
val location = address
|
val location = address
|
||||||
.municipalitySubdivision
|
.municipalitySubdivision
|
||||||
?.takeIf { it.isNotBlank() }
|
?.takeIf { it.isNotBlank() }
|
||||||
?: address.municipality
|
?: address.municipality
|
||||||
location ?: throw IOException("No location municipalitySubdivision or municipality")
|
location ?: throw IOException("No location municipalitySubdivision or municipality")
|
||||||
}
|
}
|
||||||
|
|
||||||
LocationType.City -> {
|
LocationType.City -> {
|
||||||
address.municipality ?: throw IOException("No location municipality")
|
address.municipality ?: throw IOException("No location municipality")
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ class LocationProviderImpl(
|
|||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
private suspend fun getAFreshLocation(): Location? {
|
private suspend fun getAFreshLocation(): Location? {
|
||||||
return client.getCurrentLocation(PRIORITY_LOW_POWER, object : CancellationToken() {
|
return client.getCurrentLocation(Priority.PRIORITY_LOW_POWER, object : CancellationToken() {
|
||||||
override fun isCancellationRequested(): Boolean = false
|
override fun isCancellationRequested(): Boolean = false
|
||||||
override fun onCanceledRequested(p0: OnTokenCanceledListener): CancellationToken = this
|
override fun onCanceledRequested(p0: OnTokenCanceledListener): CancellationToken = this
|
||||||
}).await()
|
}).await()
|
||||||
@@ -84,20 +84,30 @@ class LocationProviderImpl(
|
|||||||
|
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
val callback = object : LocationCallback() {
|
val callback = object : LocationCallback() {
|
||||||
override fun onLocationResult(p0: LocationResult?) {
|
override fun onLocationResult(p0: LocationResult) {
|
||||||
client.removeLocationUpdates(this)
|
client.removeLocationUpdates(this)
|
||||||
cont.resume(p0?.lastLocation)
|
cont.resume(p0.lastLocation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
with(locationManager!!) {
|
with(locationManager!!) {
|
||||||
when {
|
when {
|
||||||
isProviderEnabled(LocationManager.GPS_PROVIDER) -> {
|
isProviderEnabled(LocationManager.GPS_PROVIDER) -> {
|
||||||
client.requestLocationUpdates(createLocationRequest(PRIORITY_HIGH_ACCURACY), callback, looper)
|
client.requestLocationUpdates(
|
||||||
|
createLocationRequest(Priority.PRIORITY_HIGH_ACCURACY),
|
||||||
|
callback,
|
||||||
|
looper
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
isProviderEnabled(LocationManager.NETWORK_PROVIDER) -> {
|
isProviderEnabled(LocationManager.NETWORK_PROVIDER) -> {
|
||||||
client.requestLocationUpdates(createLocationRequest(PRIORITY_LOW_POWER), callback, looper)
|
client.requestLocationUpdates(
|
||||||
|
createLocationRequest(Priority.PRIORITY_LOW_POWER),
|
||||||
|
callback,
|
||||||
|
looper
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
cont.resume(null)
|
cont.resume(null)
|
||||||
}
|
}
|
||||||
@@ -108,7 +118,7 @@ class LocationProviderImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createLocationRequest(priority: Int) = LocationRequest.create()
|
private fun createLocationRequest(priority: Int) = LocationRequest.create()
|
||||||
.setPriority(priority)
|
.setPriority(priority)
|
||||||
.setNumUpdates(1)
|
.setNumUpdates(1)
|
||||||
.setExpirationDuration(1000)
|
.setExpirationDuration(1000)
|
||||||
}
|
}
|
||||||
@@ -8,16 +8,16 @@ open class BaseNetworkModule {
|
|||||||
// Declare the method we want/can change (no annotations)
|
// Declare the method we want/can change (no annotations)
|
||||||
open fun baseUrl() = "/"
|
open fun baseUrl() = "/"
|
||||||
|
|
||||||
inline fun <reified T: Api> invoke(
|
inline fun <reified T : Api> invoke(
|
||||||
vararg interceptors: Interceptor
|
vararg interceptors: Interceptor
|
||||||
): Api {
|
): Api {
|
||||||
|
|
||||||
val okHttpClient = buildOkHttpClient(*interceptors)
|
val okHttpClient = buildOkHttpClient(*interceptors)
|
||||||
|
|
||||||
return createRetrofit(
|
return createRetrofit(
|
||||||
baseUrl(),
|
baseUrl(),
|
||||||
okHttpClient,
|
okHttpClient,
|
||||||
T::class.java
|
T::class.java
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,8 @@ import java.io.IOException
|
|||||||
|
|
||||||
abstract class ResponseUnwrap {
|
abstract class ResponseUnwrap {
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
suspend fun <T : Any> responseUnwrap(
|
suspend fun <T : Any> responseUnwrap(
|
||||||
call: suspend () -> Response<T>
|
call: suspend () -> Response<T>
|
||||||
): T {
|
): T {
|
||||||
|
|
||||||
val response = call.invoke()
|
val response = call.invoke()
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import retrofit2.http.GET
|
|||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
|
||||||
interface WeatherApi: Api {
|
interface WeatherApi : Api {
|
||||||
|
|
||||||
@GET("onecall?")
|
@GET("onecall?")
|
||||||
suspend fun getFromApi(
|
suspend fun getFromApi(
|
||||||
@Query("lat") query: String,
|
@Query("lat") query: String,
|
||||||
@Query("lon") lon: String,
|
@Query("lon") lon: String,
|
||||||
@Query("exclude") exclude: String = "minutely",
|
@Query("exclude") exclude: String = "minutely",
|
||||||
@Query("units") units: String = "metric"
|
@Query("units") units: String = "metric"
|
||||||
): Response<WeatherResponse>
|
): Response<WeatherResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class NetworkConnectionInterceptor(
|
|||||||
private val applicationContext = context.applicationContext
|
private val applicationContext = context.applicationContext
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
|
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
|
||||||
if (!isInternetAvailable(applicationContext)){
|
if (!isInternetAvailable(applicationContext)) {
|
||||||
throw IOException("Make sure you have an active data connection")
|
throw IOException("Make sure you have an active data connection")
|
||||||
}
|
}
|
||||||
return chain.proceed(chain.request())
|
return chain.proceed(chain.request())
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import okhttp3.Response
|
|||||||
/**
|
/**
|
||||||
* Interceptor used to add default query parameters to api calls
|
* Interceptor used to add default query parameters to api calls
|
||||||
*/
|
*/
|
||||||
class QueryParamsInterceptor : Interceptor{
|
class QueryParamsInterceptor : Interceptor {
|
||||||
|
|
||||||
val id = BuildConfig.ParamOne
|
val id = BuildConfig.ParamOne
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ class QueryParamsInterceptor : Interceptor{
|
|||||||
val original = chain.request()
|
val original = chain.request()
|
||||||
|
|
||||||
val url = original.url.newBuilder()
|
val url = original.url.newBuilder()
|
||||||
.addQueryParameter("appid", id)
|
.addQueryParameter("appid", id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
// Request customization: add request headers
|
// Request customization: add request headers
|
||||||
val requestBuilder = original.newBuilder().url(url)
|
val requestBuilder = original.newBuilder().url(url)
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import retrofit2.converter.gson.GsonConverterFactory
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
val loggingInterceptor = HttpLoggingInterceptor().apply {
|
val loggingInterceptor = HttpLoggingInterceptor().apply {
|
||||||
level = HttpLoggingInterceptor.Level.BODY
|
level = HttpLoggingInterceptor.Level.BODY
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildOkHttpClient(
|
fun buildOkHttpClient(
|
||||||
vararg interceptor: Interceptor,
|
vararg interceptor: Interceptor,
|
||||||
timeoutSeconds: Long = 30L
|
timeoutSeconds: Long = 30L
|
||||||
): OkHttpClient {
|
): OkHttpClient {
|
||||||
|
|
||||||
val builder = OkHttpClient.Builder()
|
val builder = OkHttpClient.Builder()
|
||||||
@@ -28,21 +28,21 @@ fun buildOkHttpClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.connectTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
builder.connectTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
||||||
.writeTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
.writeTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
||||||
.readTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
.readTimeout(timeoutSeconds, TimeUnit.SECONDS)
|
||||||
|
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> createRetrofit(
|
fun <T> createRetrofit(
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
okHttpClient: OkHttpClient,
|
okHttpClient: OkHttpClient,
|
||||||
service: Class<T>
|
service: Class<T>
|
||||||
): T {
|
): T {
|
||||||
return Retrofit.Builder()
|
return Retrofit.Builder()
|
||||||
.client(okHttpClient)
|
.client(okHttpClient)
|
||||||
.baseUrl(baseUrl)
|
.baseUrl(baseUrl)
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
.build()
|
.build()
|
||||||
.create(service)
|
.create(service)
|
||||||
}
|
}
|
||||||
@@ -4,45 +4,45 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class Current(
|
data class Current(
|
||||||
|
|
||||||
@field:SerializedName("sunrise")
|
@field:SerializedName("sunrise")
|
||||||
val sunrise: Int? = null,
|
val sunrise: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("temp")
|
@field:SerializedName("temp")
|
||||||
val temp: Double? = null,
|
val temp: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("visibility")
|
@field:SerializedName("visibility")
|
||||||
val visibility: Int? = null,
|
val visibility: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("uvi")
|
@field:SerializedName("uvi")
|
||||||
val uvi: Double? = null,
|
val uvi: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("pressure")
|
@field:SerializedName("pressure")
|
||||||
val pressure: Int? = null,
|
val pressure: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("clouds")
|
@field:SerializedName("clouds")
|
||||||
val clouds: Int? = null,
|
val clouds: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("feels_like")
|
@field:SerializedName("feels_like")
|
||||||
val feelsLike: Double? = null,
|
val feelsLike: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("dt")
|
@field:SerializedName("dt")
|
||||||
val dt: Int? = null,
|
val dt: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("wind_deg")
|
@field:SerializedName("wind_deg")
|
||||||
val windDeg: Int? = null,
|
val windDeg: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("dew_point")
|
@field:SerializedName("dew_point")
|
||||||
val dewPoint: Double? = null,
|
val dewPoint: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("sunset")
|
@field:SerializedName("sunset")
|
||||||
val sunset: Int? = null,
|
val sunset: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("weather")
|
@field:SerializedName("weather")
|
||||||
val weather: List<WeatherItem?>? = null,
|
val weather: List<WeatherItem?>? = null,
|
||||||
|
|
||||||
@field:SerializedName("humidity")
|
@field:SerializedName("humidity")
|
||||||
val humidity: Int? = null,
|
val humidity: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("wind_speed")
|
@field:SerializedName("wind_speed")
|
||||||
val windSpeed: Double? = null
|
val windSpeed: Double? = null
|
||||||
)
|
)
|
||||||
@@ -4,48 +4,48 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class DailyItem(
|
data class DailyItem(
|
||||||
|
|
||||||
@field:SerializedName("sunrise")
|
@field:SerializedName("sunrise")
|
||||||
val sunrise: Int? = null,
|
val sunrise: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("temp")
|
@field:SerializedName("temp")
|
||||||
val temp: Temp? = null,
|
val temp: Temp? = null,
|
||||||
|
|
||||||
@field:SerializedName("uvi")
|
@field:SerializedName("uvi")
|
||||||
val uvi: Double? = null,
|
val uvi: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("pressure")
|
@field:SerializedName("pressure")
|
||||||
val pressure: Int? = null,
|
val pressure: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("clouds")
|
@field:SerializedName("clouds")
|
||||||
val clouds: Int? = null,
|
val clouds: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("feels_like")
|
@field:SerializedName("feels_like")
|
||||||
val feelsLike: FeelsLike? = null,
|
val feelsLike: FeelsLike? = null,
|
||||||
|
|
||||||
@field:SerializedName("dt")
|
@field:SerializedName("dt")
|
||||||
val dt: Int? = null,
|
val dt: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("pop")
|
@field:SerializedName("pop")
|
||||||
val pop: Double? = null,
|
val pop: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("wind_deg")
|
@field:SerializedName("wind_deg")
|
||||||
val windDeg: Int? = null,
|
val windDeg: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("dew_point")
|
@field:SerializedName("dew_point")
|
||||||
val dewPoint: Double? = null,
|
val dewPoint: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("sunset")
|
@field:SerializedName("sunset")
|
||||||
val sunset: Int? = null,
|
val sunset: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("weather")
|
@field:SerializedName("weather")
|
||||||
val weather: List<WeatherItem?>? = null,
|
val weather: List<WeatherItem?>? = null,
|
||||||
|
|
||||||
@field:SerializedName("humidity")
|
@field:SerializedName("humidity")
|
||||||
val humidity: Int? = null,
|
val humidity: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("wind_speed")
|
@field:SerializedName("wind_speed")
|
||||||
val windSpeed: Double? = null,
|
val windSpeed: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("rain")
|
@field:SerializedName("rain")
|
||||||
val rain: Double? = null
|
val rain: Double? = null
|
||||||
)
|
)
|
||||||
@@ -4,15 +4,15 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class FeelsLike(
|
data class FeelsLike(
|
||||||
|
|
||||||
@field:SerializedName("eve")
|
@field:SerializedName("eve")
|
||||||
val eve: Double? = null,
|
val eve: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("night")
|
@field:SerializedName("night")
|
||||||
val night: Double? = null,
|
val night: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("day")
|
@field:SerializedName("day")
|
||||||
val day: Double? = null,
|
val day: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("morn")
|
@field:SerializedName("morn")
|
||||||
val morn: Double? = null
|
val morn: Double? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,45 +4,45 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class Hour(
|
data class Hour(
|
||||||
|
|
||||||
@field:SerializedName("sunrise")
|
@field:SerializedName("sunrise")
|
||||||
val sunrise: Int? = null,
|
val sunrise: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("temp")
|
@field:SerializedName("temp")
|
||||||
val temp: Double? = null,
|
val temp: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("visibility")
|
@field:SerializedName("visibility")
|
||||||
val visibility: Int? = null,
|
val visibility: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("uvi")
|
@field:SerializedName("uvi")
|
||||||
val uvi: Double? = null,
|
val uvi: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("pressure")
|
@field:SerializedName("pressure")
|
||||||
val pressure: Int? = null,
|
val pressure: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("clouds")
|
@field:SerializedName("clouds")
|
||||||
val clouds: Int? = null,
|
val clouds: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("feels_like")
|
@field:SerializedName("feels_like")
|
||||||
val feelsLike: Double? = null,
|
val feelsLike: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("dt")
|
@field:SerializedName("dt")
|
||||||
val dt: Int? = null,
|
val dt: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("wind_deg")
|
@field:SerializedName("wind_deg")
|
||||||
val windDeg: Int? = null,
|
val windDeg: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("dew_point")
|
@field:SerializedName("dew_point")
|
||||||
val dewPoint: Double? = null,
|
val dewPoint: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("sunset")
|
@field:SerializedName("sunset")
|
||||||
val sunset: Int? = null,
|
val sunset: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("weather")
|
@field:SerializedName("weather")
|
||||||
val weather: List<WeatherItem?>? = null,
|
val weather: List<WeatherItem?>? = null,
|
||||||
|
|
||||||
@field:SerializedName("humidity")
|
@field:SerializedName("humidity")
|
||||||
val humidity: Int? = null,
|
val humidity: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("wind_speed")
|
@field:SerializedName("wind_speed")
|
||||||
val windSpeed: Double? = null
|
val windSpeed: Double? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class Response(
|
data class Response(
|
||||||
|
|
||||||
@field:SerializedName("current")
|
@field:SerializedName("current")
|
||||||
val current: Current? = null,
|
val current: Current? = null,
|
||||||
|
|
||||||
@field:SerializedName("timezone")
|
@field:SerializedName("timezone")
|
||||||
val timezone: String? = null,
|
val timezone: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("timezone_offset")
|
@field:SerializedName("timezone_offset")
|
||||||
val timezoneOffset: Int? = null,
|
val timezoneOffset: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("daily")
|
@field:SerializedName("daily")
|
||||||
val daily: List<DailyItem?>? = null,
|
val daily: List<DailyItem?>? = null,
|
||||||
|
|
||||||
@field:SerializedName("lon")
|
@field:SerializedName("lon")
|
||||||
val lon: Double? = null,
|
val lon: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("lat")
|
@field:SerializedName("lat")
|
||||||
val lat: Double? = null
|
val lat: Double? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class Temp(
|
data class Temp(
|
||||||
|
|
||||||
@field:SerializedName("min")
|
@field:SerializedName("min")
|
||||||
val min: Double? = null,
|
val min: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("max")
|
@field:SerializedName("max")
|
||||||
val max: Double? = null,
|
val max: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("eve")
|
@field:SerializedName("eve")
|
||||||
val eve: Double? = null,
|
val eve: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("night")
|
@field:SerializedName("night")
|
||||||
val night: Double? = null,
|
val night: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("day")
|
@field:SerializedName("day")
|
||||||
val day: Double? = null,
|
val day: Double? = null,
|
||||||
|
|
||||||
@field:SerializedName("morn")
|
@field:SerializedName("morn")
|
||||||
val morn: Double? = null
|
val morn: Double? = null
|
||||||
)
|
)
|
||||||
@@ -4,15 +4,15 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class WeatherItem(
|
data class WeatherItem(
|
||||||
|
|
||||||
@field:SerializedName("icon")
|
@field:SerializedName("icon")
|
||||||
val icon: String? = null,
|
val icon: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("description")
|
@field:SerializedName("description")
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("main")
|
@field:SerializedName("main")
|
||||||
val main: String? = null,
|
val main: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("id")
|
@field:SerializedName("id")
|
||||||
val id: Int? = null
|
val id: Int? = null
|
||||||
)
|
)
|
||||||
@@ -4,25 +4,25 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class WeatherResponse(
|
data class WeatherResponse(
|
||||||
|
|
||||||
@field:SerializedName("current")
|
@field:SerializedName("current")
|
||||||
val current: Current? = null,
|
val current: Current? = null,
|
||||||
|
|
||||||
@field:SerializedName("timezone")
|
@field:SerializedName("timezone")
|
||||||
val timezone: String? = null,
|
val timezone: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("timezone_offset")
|
@field:SerializedName("timezone_offset")
|
||||||
val timezoneOffset: Int? = null,
|
val timezoneOffset: Int? = null,
|
||||||
|
|
||||||
@field:SerializedName("hourly")
|
@field:SerializedName("hourly")
|
||||||
val hourly: List<Hour>? = null,
|
val hourly: List<Hour>? = null,
|
||||||
|
|
||||||
@field:SerializedName("daily")
|
@field:SerializedName("daily")
|
||||||
val daily: List<DailyItem>? = null,
|
val daily: List<DailyItem>? = null,
|
||||||
|
|
||||||
@field:SerializedName("lon")
|
@field:SerializedName("lon")
|
||||||
val lon: Double = 0.00,
|
val lon: Double = 0.00,
|
||||||
|
|
||||||
@field:SerializedName("lat")
|
@field:SerializedName("lat")
|
||||||
val lat: Double = 0.00
|
val lat: Double = 0.00
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
|||||||
* Shared preferences to save & load last timestamp
|
* Shared preferences to save & load last timestamp
|
||||||
*/
|
*/
|
||||||
const val LOCATION_CONST = "location_"
|
const val LOCATION_CONST = "location_"
|
||||||
|
|
||||||
class PreferenceProvider(
|
class PreferenceProvider(
|
||||||
context: Context
|
context: Context
|
||||||
){
|
) {
|
||||||
|
|
||||||
private val appContext = context.applicationContext
|
private val appContext = context.applicationContext
|
||||||
|
|
||||||
@@ -20,30 +21,30 @@ class PreferenceProvider(
|
|||||||
|
|
||||||
fun saveLastSavedAt(locationName: String) {
|
fun saveLastSavedAt(locationName: String) {
|
||||||
preference.edit().putLong(
|
preference.edit().putLong(
|
||||||
locationName,
|
locationName,
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
).apply()
|
).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLastSavedAt(locationName: String): Long? {
|
fun getLastSavedAt(locationName: String): Long {
|
||||||
return preference.getLong(locationName, 0L)
|
return preference.getLong(locationName, 0L)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllKeys() = preference.all.keys.apply {
|
fun getAllKeys() = preference.all.keys.apply {
|
||||||
remove(CURRENT_LOCATION)
|
remove(CURRENT_LOCATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteLocation(locationName: String){
|
fun deleteLocation(locationName: String) {
|
||||||
preference.edit().remove(locationName).apply()
|
preference.edit().remove(locationName).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNotificationsEnabled(): Boolean = preference.getBoolean("notif_boolean", true)
|
fun isNotificationsEnabled(): Boolean = preference.getBoolean("notif_boolean", true)
|
||||||
|
|
||||||
fun setFirstTimeRun(){
|
fun setFirstTimeRun() {
|
||||||
preference.edit().putBoolean("FIRST_TIME_RUN", false).apply()
|
preference.edit().putBoolean("FIRST_TIME_RUN", false).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isWidgetBlackground(): Boolean {
|
fun isWidgetBackground(): Boolean {
|
||||||
return preference.getBoolean("widget_black_background", false)
|
return preference.getBoolean("widget_black_background", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface Repository {
|
|||||||
suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem)
|
suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem)
|
||||||
suspend fun saveWeatherListToRoom(list: List<EntityItem>)
|
suspend fun saveWeatherListToRoom(list: List<EntityItem>)
|
||||||
fun loadRoomWeatherLiveData(): LiveData<List<EntityItem>>
|
fun loadRoomWeatherLiveData(): LiveData<List<EntityItem>>
|
||||||
suspend fun loadWeatherList() : List<String>
|
suspend fun loadWeatherList(): List<String>
|
||||||
fun loadCurrentWeatherFromRoom(id: String): LiveData<EntityItem>
|
fun loadCurrentWeatherFromRoom(id: String): LiveData<EntityItem>
|
||||||
suspend fun loadSingleCurrentWeatherFromRoom(id: String): EntityItem
|
suspend fun loadSingleCurrentWeatherFromRoom(id: String): EntityItem
|
||||||
fun isSearchValid(locationName: String): Boolean
|
fun isSearchValid(locationName: String): Boolean
|
||||||
|
|||||||
@@ -23,34 +23,34 @@ class RepositoryImpl(
|
|||||||
return responseUnwrap { api.getFromApi(lat, long) }
|
return responseUnwrap { api.getFromApi(lat, long) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem){
|
override suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem) {
|
||||||
db.getSimpleDao().upsertFullWeather(entityItem)
|
db.getSimpleDao().upsertFullWeather(entityItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun saveWeatherListToRoom(
|
override suspend fun saveWeatherListToRoom(
|
||||||
list: List<EntityItem>
|
list: List<EntityItem>
|
||||||
){
|
) {
|
||||||
db.getSimpleDao().upsertListOfFullWeather(list)
|
db.getSimpleDao().upsertListOfFullWeather(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadRoomWeatherLiveData() = db.getSimpleDao().getAllFullWeatherWithoutCurrent()
|
override fun loadRoomWeatherLiveData() = db.getSimpleDao().getAllFullWeatherWithoutCurrent()
|
||||||
|
|
||||||
override suspend fun loadWeatherList() : List<String>{
|
override suspend fun loadWeatherList(): List<String> {
|
||||||
return db.getSimpleDao()
|
return db.getSimpleDao()
|
||||||
.getWeatherListWithoutCurrent()
|
.getWeatherListWithoutCurrent()
|
||||||
.map { it.id }
|
.map { it.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadCurrentWeatherFromRoom(id: String)
|
override fun loadCurrentWeatherFromRoom(id: String) =
|
||||||
= db.getSimpleDao().getCurrentFullWeather(id)
|
db.getSimpleDao().getCurrentFullWeather(id)
|
||||||
|
|
||||||
override suspend fun loadSingleCurrentWeatherFromRoom(id: String)
|
override suspend fun loadSingleCurrentWeatherFromRoom(id: String) =
|
||||||
= db.getSimpleDao().getCurrentFullWeatherSingle(id)
|
db.getSimpleDao().getCurrentFullWeatherSingle(id)
|
||||||
|
|
||||||
override fun isSearchValid(locationName: String): Boolean {
|
override fun isSearchValid(locationName: String): Boolean {
|
||||||
val lastSaved = prefs
|
val lastSaved = prefs
|
||||||
.getLastSavedAt("$LOCATION_CONST$locationName")
|
.getLastSavedAt("$LOCATION_CONST$locationName")
|
||||||
?: return true
|
?: return true
|
||||||
val difference = System.currentTimeMillis() - lastSaved
|
val difference = System.currentTimeMillis() - lastSaved
|
||||||
|
|
||||||
return difference > FALLBACK_TIME
|
return difference > FALLBACK_TIME
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package com.appttude.h_mal.atlas_weather.data.repository
|
|||||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||||
|
|
||||||
class SettingsRepositoryImpl(
|
class SettingsRepositoryImpl(
|
||||||
private val prefs: PreferenceProvider
|
private val prefs: PreferenceProvider
|
||||||
) : SettingsRepository{
|
) : SettingsRepository {
|
||||||
|
|
||||||
override fun isNotificationsEnabled(): Boolean = prefs.isNotificationsEnabled()
|
override fun isNotificationsEnabled(): Boolean = prefs.isNotificationsEnabled()
|
||||||
|
|
||||||
override fun setFirstTime() = prefs.setFirstTimeRun()
|
override fun setFirstTime() = prefs.setFirstTimeRun()
|
||||||
|
|
||||||
override fun isBlackBackground() = prefs.isWidgetBlackground()
|
override fun isBlackBackground() = prefs.isWidgetBackground()
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,9 @@ import androidx.room.TypeConverters
|
|||||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [EntityItem::class],
|
entities = [EntityItem::class],
|
||||||
version = 1,
|
version = 1,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
@TypeConverters(Converter::class)
|
@TypeConverters(Converter::class)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
@@ -31,12 +31,12 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildDatabase(context: Context) =
|
private fun buildDatabase(context: Context) =
|
||||||
Room.databaseBuilder(
|
Room.databaseBuilder(
|
||||||
context.applicationContext,
|
context.applicationContext,
|
||||||
AppDatabase::class.java,
|
AppDatabase::class.java,
|
||||||
"MyDatabase.db"
|
"MyDatabase.db"
|
||||||
).addTypeConverter(Converter(context))
|
).addTypeConverter(Converter(context))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,16 +18,16 @@ interface WeatherDao {
|
|||||||
fun upsertListOfFullWeather(items: List<EntityItem>)
|
fun upsertListOfFullWeather(items: List<EntityItem>)
|
||||||
|
|
||||||
@Query("SELECT * FROM EntityItem WHERE id = :userId LIMIT 1")
|
@Query("SELECT * FROM EntityItem WHERE id = :userId LIMIT 1")
|
||||||
fun getCurrentFullWeather(userId: String) : LiveData<EntityItem>
|
fun getCurrentFullWeather(userId: String): LiveData<EntityItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM EntityItem WHERE id = :userId LIMIT 1")
|
@Query("SELECT * FROM EntityItem WHERE id = :userId LIMIT 1")
|
||||||
fun getCurrentFullWeatherSingle(userId: String) : EntityItem
|
fun getCurrentFullWeatherSingle(userId: String): EntityItem
|
||||||
|
|
||||||
@Query("SELECT * FROM EntityItem WHERE id != :id")
|
@Query("SELECT * FROM EntityItem WHERE id != :id")
|
||||||
fun getAllFullWeatherWithoutCurrent(id: String = CURRENT_LOCATION) : LiveData<List<EntityItem>>
|
fun getAllFullWeatherWithoutCurrent(id: String = CURRENT_LOCATION): LiveData<List<EntityItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM EntityItem WHERE id != :id")
|
@Query("SELECT * FROM EntityItem WHERE id != :id")
|
||||||
fun getWeatherListWithoutCurrent(id: String = CURRENT_LOCATION) : List<EntityItem>
|
fun getWeatherListWithoutCurrent(id: String = CURRENT_LOCATION): List<EntityItem>
|
||||||
|
|
||||||
@Query("DELETE FROM EntityItem WHERE id = :userId")
|
@Query("DELETE FROM EntityItem WHERE id = :userId")
|
||||||
fun deleteEntry(userId: String): Int
|
fun deleteEntry(userId: String): Int
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
|||||||
|
|
||||||
|
|
||||||
const val CURRENT_LOCATION = "CurrentLocation"
|
const val CURRENT_LOCATION = "CurrentLocation"
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
data class EntityItem(
|
data class EntityItem(
|
||||||
@PrimaryKey(autoGenerate = false)
|
@PrimaryKey(autoGenerate = false)
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.helper
|
package com.appttude.h_mal.atlas_weather.helper
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import java.lang.reflect.ParameterizedType
|
import java.lang.reflect.ParameterizedType
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ import kotlin.coroutines.suspendCoroutine
|
|||||||
|
|
||||||
|
|
||||||
class ServicesHelper(
|
class ServicesHelper(
|
||||||
private val repository: Repository,
|
private val repository: Repository,
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val locationProvider: LocationProvider
|
private val locationProvider: LocationProvider
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
|
@RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||||
@@ -40,8 +40,9 @@ class ServicesHelper(
|
|||||||
val latLong = locationProvider.getCurrentLatLong()
|
val latLong = locationProvider.getCurrentLatLong()
|
||||||
// Get weather from api
|
// Get weather from api
|
||||||
val weather = repository
|
val weather = repository
|
||||||
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
|
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
|
||||||
val currentLocation = locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon)
|
val currentLocation =
|
||||||
|
locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon)
|
||||||
val fullWeather = FullWeather(weather).apply {
|
val fullWeather = FullWeather(weather).apply {
|
||||||
temperatureUnit = "°C"
|
temperatureUnit = "°C"
|
||||||
locationString = currentLocation
|
locationString = currentLocation
|
||||||
@@ -61,7 +62,7 @@ class ServicesHelper(
|
|||||||
return try {
|
return try {
|
||||||
val result = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
val result = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
||||||
val epoc = System.currentTimeMillis()
|
val epoc = System.currentTimeMillis()
|
||||||
|
|
||||||
result.weather.let {
|
result.weather.let {
|
||||||
val bitmap = it.current?.icon
|
val bitmap = it.current?.icon
|
||||||
val location = locationProvider.getLocationNameFromLatLong(it.lat, it.lon)
|
val location = locationProvider.getLocationNameFromLatLong(it.lat, it.lon)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ data class Forecast(
|
|||||||
val sunrise: String?,
|
val sunrise: String?,
|
||||||
val sunset: String?,
|
val sunset: String?,
|
||||||
val cloud: String?
|
val cloud: String?
|
||||||
): Parcelable{
|
) : Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
parcel.readString(),
|
parcel.readString(),
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ data class WeatherDisplay(
|
|||||||
val lat: Double = 0.00,
|
val lat: Double = 0.00,
|
||||||
val lon: Double = 0.00,
|
val lon: Double = 0.00,
|
||||||
var displayName: String?
|
var displayName: String?
|
||||||
): Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.model.types
|
package com.appttude.h_mal.atlas_weather.model.types
|
||||||
|
|
||||||
enum class LocationType{
|
enum class LocationType {
|
||||||
City,
|
City,
|
||||||
Town
|
Town
|
||||||
}
|
}
|
||||||
@@ -5,49 +5,49 @@ import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
|||||||
|
|
||||||
|
|
||||||
data class DailyWeather(
|
data class DailyWeather(
|
||||||
val dt: Int?,
|
val dt: Int?,
|
||||||
val sunrise: Int?,
|
val sunrise: Int?,
|
||||||
val sunset: Int?,
|
val sunset: Int?,
|
||||||
val min: Double? = null,
|
val min: Double? = null,
|
||||||
val max: Double? = null,
|
val max: Double? = null,
|
||||||
val average: Double? = null,
|
val average: Double? = null,
|
||||||
var feelsLike: Double?,
|
var feelsLike: Double?,
|
||||||
val pressure: Int?,
|
val pressure: Int?,
|
||||||
val humidity: Int?,
|
val humidity: Int?,
|
||||||
val dewPoint: Double?,
|
val dewPoint: Double?,
|
||||||
val windSpeed: Double?,
|
val windSpeed: Double?,
|
||||||
val windDeg: Int?,
|
val windDeg: Int?,
|
||||||
val icon: String? = null,
|
val icon: String? = null,
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
val main: String? = null,
|
val main: String? = null,
|
||||||
val id: Int? = null,
|
val id: Int? = null,
|
||||||
val clouds: Int?,
|
val clouds: Int?,
|
||||||
val pop: Double?,
|
val pop: Double?,
|
||||||
val uvi: Double?,
|
val uvi: Double?,
|
||||||
val rain: Double?
|
val rain: Double?
|
||||||
){
|
) {
|
||||||
|
|
||||||
constructor(dailyItem: DailyItem): this(
|
constructor(dailyItem: DailyItem) : this(
|
||||||
dailyItem.dt,
|
dailyItem.dt,
|
||||||
dailyItem.sunrise,
|
dailyItem.sunrise,
|
||||||
dailyItem.sunset,
|
dailyItem.sunset,
|
||||||
dailyItem.temp?.min,
|
dailyItem.temp?.min,
|
||||||
dailyItem.temp?.max,
|
dailyItem.temp?.max,
|
||||||
dailyItem.temp?.day,
|
dailyItem.temp?.day,
|
||||||
dailyItem.feelsLike?.day,
|
dailyItem.feelsLike?.day,
|
||||||
dailyItem.pressure,
|
dailyItem.pressure,
|
||||||
dailyItem.humidity,
|
dailyItem.humidity,
|
||||||
dailyItem.dewPoint,
|
dailyItem.dewPoint,
|
||||||
dailyItem.windSpeed,
|
dailyItem.windSpeed,
|
||||||
dailyItem.windDeg,
|
dailyItem.windDeg,
|
||||||
generateIconUrlString(dailyItem.weather?.getOrNull(0)?.icon),
|
generateIconUrlString(dailyItem.weather?.getOrNull(0)?.icon),
|
||||||
dailyItem.weather?.get(0)?.description,
|
dailyItem.weather?.get(0)?.description,
|
||||||
dailyItem.weather?.get(0)?.main,
|
dailyItem.weather?.get(0)?.main,
|
||||||
dailyItem.weather?.get(0)?.id,
|
dailyItem.weather?.get(0)?.id,
|
||||||
dailyItem.clouds,
|
dailyItem.clouds,
|
||||||
dailyItem.pop,
|
dailyItem.pop,
|
||||||
dailyItem.uvi,
|
dailyItem.uvi,
|
||||||
dailyItem.rain
|
dailyItem.rain
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,26 +3,26 @@ package com.appttude.h_mal.atlas_weather.model.weather
|
|||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
||||||
|
|
||||||
data class FullWeather(
|
data class FullWeather(
|
||||||
val current: Current? = null,
|
val current: Current? = null,
|
||||||
val timezone: String? = null,
|
val timezone: String? = null,
|
||||||
val timezoneOffset: Int? = null,
|
val timezoneOffset: Int? = null,
|
||||||
val hourly: List<Hour>? = null,
|
val hourly: List<Hour>? = null,
|
||||||
val daily: List<DailyWeather>? = null,
|
val daily: List<DailyWeather>? = null,
|
||||||
val lon: Double = 0.00,
|
val lon: Double = 0.00,
|
||||||
val lat: Double = 0.00,
|
val lat: Double = 0.00,
|
||||||
var locationString: String? = null,
|
var locationString: String? = null,
|
||||||
var temperatureUnit: String? = null
|
var temperatureUnit: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
constructor(weatherResponse: WeatherResponse): this(
|
constructor(weatherResponse: WeatherResponse) : this(
|
||||||
weatherResponse.current?.let { Current(it) },
|
weatherResponse.current?.let { Current(it) },
|
||||||
weatherResponse.timezone,
|
weatherResponse.timezone,
|
||||||
weatherResponse.timezoneOffset,
|
weatherResponse.timezoneOffset,
|
||||||
weatherResponse.hourly?.subList(0,23)?.map { Hour(it) },
|
weatherResponse.hourly?.subList(0, 23)?.map { Hour(it) },
|
||||||
weatherResponse.daily?.map { DailyWeather(it) },
|
weatherResponse.daily?.map { DailyWeather(it) },
|
||||||
weatherResponse.lon,
|
weatherResponse.lon,
|
||||||
weatherResponse.lat
|
weatherResponse.lat
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ data class Hour(
|
|||||||
val dt: Int? = null,
|
val dt: Int? = null,
|
||||||
val temp: Double? = null,
|
val temp: Double? = null,
|
||||||
val icon: String? = null
|
val icon: String? = null
|
||||||
): Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||||
|
|||||||
@@ -3,26 +3,26 @@ package com.appttude.h_mal.atlas_weather.model.widget
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
|
||||||
data class WidgetData(
|
data class WidgetData(
|
||||||
val location: String?,
|
val location: String?,
|
||||||
val icon: String?,
|
val icon: String?,
|
||||||
val currentTemp: String?,
|
val currentTemp: String?,
|
||||||
val timeStamp: Long
|
val timeStamp: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
data class InnerWidgetData(
|
data class InnerWidgetData(
|
||||||
val date: String?,
|
val date: String?,
|
||||||
val icon: Bitmap?,
|
val icon: Bitmap?,
|
||||||
val highTemp: String?
|
val highTemp: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
data class InnerWidgetCellData(
|
data class InnerWidgetCellData(
|
||||||
val date: String?,
|
val date: String?,
|
||||||
val icon: String?,
|
val icon: String?,
|
||||||
val highTemp: String?
|
val highTemp: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class WidgetWeatherCollection(
|
data class WidgetWeatherCollection(
|
||||||
val widgetData: WidgetData,
|
val widgetData: WidgetData,
|
||||||
val forecast: List<InnerWidgetCellData>
|
val forecast: List<InnerWidgetCellData>
|
||||||
)
|
)
|
||||||
@@ -14,11 +14,11 @@ import androidx.lifecycle.Observer
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
|
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
|
||||||
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
|
||||||
import com.appttude.h_mal.atlas_weather.utils.Event
|
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.displayToast
|
||||||
import com.appttude.h_mal.atlas_weather.utils.hide
|
import com.appttude.h_mal.atlas_weather.utils.hide
|
||||||
import com.appttude.h_mal.atlas_weather.utils.show
|
import com.appttude.h_mal.atlas_weather.utils.show
|
||||||
|
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -27,6 +27,7 @@ import org.kodein.di.android.x.kodein
|
|||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
@Suppress("EmptyMethod", "EmptyMethod")
|
||||||
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId),
|
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId),
|
||||||
KodeinAware {
|
KodeinAware {
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.ui
|
package com.appttude.h_mal.atlas_weather.ui
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@@ -12,7 +10,7 @@ import androidx.navigation.ui.setupActionBarWithNavController
|
|||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import kotlinx.android.synthetic.main.activity_main_navigation.*
|
import kotlinx.android.synthetic.main.activity_main_navigation.toolbar
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
navHost = supportFragmentManager
|
navHost = supportFragmentManager
|
||||||
.findFragmentById(R.id.container) as NavHostFragment
|
.findFragmentById(R.id.container) as NavHostFragment
|
||||||
val navController = navHost.navController
|
val navController = navHost.navController
|
||||||
navController.setGraph(R.navigation.main_navigation)
|
navController.setGraph(R.navigation.main_navigation)
|
||||||
|
|
||||||
|
|||||||
@@ -6,31 +6,32 @@ import kotlin.coroutines.resume
|
|||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
inline fun <reified T> parcelableCreator(
|
inline fun <reified T> parcelableCreator(
|
||||||
crossinline create: (Parcel) -> T) =
|
crossinline create: (Parcel) -> T
|
||||||
object : Parcelable.Creator<T> {
|
) =
|
||||||
override fun createFromParcel(source: Parcel) = create(source)
|
object : Parcelable.Creator<T> {
|
||||||
override fun newArray(size: Int) = arrayOfNulls<T>(size)
|
override fun createFromParcel(source: Parcel) = create(source)
|
||||||
}
|
override fun newArray(size: Int) = arrayOfNulls<T>(size)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun <T : Any?> tryOrNullSuspended(
|
suspend fun <T : Any?> tryOrNullSuspended(
|
||||||
call: suspend () -> T?
|
call: suspend () -> T?
|
||||||
): T? {
|
): T? {
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
call.invoke()
|
call.invoke()
|
||||||
}catch (e: Exception){
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any?> tryOrNull(
|
fun <T : Any?> tryOrNull(
|
||||||
call: () -> T?
|
call: () -> T?
|
||||||
): T? {
|
): T? {
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
call.invoke()
|
call.invoke()
|
||||||
}catch (e: Exception){
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -46,9 +47,9 @@ fun <T : Any?> tryOrNull(
|
|||||||
*
|
*
|
||||||
* Both equal 2.
|
* Both equal 2.
|
||||||
*/
|
*/
|
||||||
suspend fun <T: Any> createSuspend(
|
suspend fun <T : Any> createSuspend(
|
||||||
call: () -> T?
|
call: () -> T?
|
||||||
): T?{
|
): T? {
|
||||||
|
|
||||||
return suspendCoroutine { cont ->
|
return suspendCoroutine { cont ->
|
||||||
cont.resume(call())
|
cont.resume(call())
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import androidx.navigation.NavDirections
|
|||||||
import androidx.navigation.Navigation
|
import androidx.navigation.Navigation
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
|
||||||
fun Fragment.navigateToFragment(newFragment: Fragment){
|
fun Fragment.navigateToFragment(newFragment: Fragment) {
|
||||||
childFragmentManager.beginTransaction()
|
childFragmentManager.beginTransaction()
|
||||||
.add(R.id.container, newFragment)
|
.add(R.id.container, newFragment)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import android.content.Context
|
|||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
|
|
||||||
fun isInternetAvailable(
|
fun isInternetAvailable(context: Context): Boolean {
|
||||||
context: Context
|
|
||||||
): Boolean {
|
|
||||||
var result = false
|
var result = false
|
||||||
val connectivityManager =
|
val connectivityManager =
|
||||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
||||||
connectivityManager?.let {
|
connectivityManager?.let {
|
||||||
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply {
|
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply {
|
||||||
result = when {
|
result = when {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.utils
|
package com.appttude.h_mal.atlas_weather.utils
|
||||||
|
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.OffsetTime
|
import java.time.OffsetTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.*
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
fun Int.toDayString(): String {
|
fun Int.toDayString(): String {
|
||||||
return try {
|
return try {
|
||||||
@@ -46,13 +46,8 @@ fun Int.toSmallDayName(): String {
|
|||||||
|
|
||||||
fun Int?.toTime(): String? {
|
fun Int?.toTime(): String? {
|
||||||
return this?.makeMilliseconds()?.let {
|
return this?.makeMilliseconds()?.let {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
OffsetTime.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC)
|
||||||
OffsetTime.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("HH:mm"))
|
.format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||||
} else {
|
|
||||||
val date = Date(it)
|
|
||||||
val format = SimpleDateFormat("HH:mm", Locale.getDefault())
|
|
||||||
format.format(date)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.utils
|
package com.appttude.h_mal.atlas_weather.utils
|
||||||
|
|
||||||
|
|
||||||
fun generateIconUrlString(icon: String?): String?{
|
fun generateIconUrlString(icon: String?): String? {
|
||||||
return icon?.let {
|
return icon?.let {
|
||||||
StringBuilder()
|
StringBuilder()
|
||||||
.append("http://openweathermap.org/img/wn/")
|
.append("http://openweathermap.org/img/wn/")
|
||||||
.append(it)
|
.append(it)
|
||||||
.append("@2x.png")
|
.append("@2x.png")
|
||||||
.toString()
|
.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ package com.appttude.h_mal.atlas_weather.utils
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@@ -30,10 +29,10 @@ fun Fragment.displayToast(message: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun ViewGroup.generateView(layoutId: Int): View = LayoutInflater
|
fun ViewGroup.generateView(layoutId: Int): View = LayoutInflater
|
||||||
.from(context)
|
.from(context)
|
||||||
.inflate(layoutId, this, false)
|
.inflate(layoutId, this, false)
|
||||||
|
|
||||||
fun ImageView.loadImage(url: String?){
|
fun ImageView.loadImage(url: String?) {
|
||||||
Picasso.get().load(url)
|
Picasso.get().load(url)
|
||||||
.placeholder(R.drawable.ic_baseline_cloud_queue_24)
|
.placeholder(R.drawable.ic_baseline_cloud_queue_24)
|
||||||
.error(R.drawable.ic_baseline_cloud_off_24)
|
.error(R.drawable.ic_baseline_cloud_off_24)
|
||||||
|
|||||||