mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
Playstore permissions declaration (#12)
* Declaration builder created Took 2 hours 34 minutes Took 2 minutes * - Fixed android S issues - Dialog box completed - Widget creation activity UI test added Took 1 hour 53 minutes * - Popup for google playstore permissions added - UI tests with stubbing added - linting clean ups - changes to fragments and base fragment Took 3 hours 57 minutes
This commit is contained in:
97
.gitignore
vendored
97
.gitignore
vendored
@@ -1,11 +1,90 @@
|
|||||||
*.iml
|
### AndroidStudio ###
|
||||||
|
# Covers files to be ignored for android development using Android Studio.
|
||||||
|
|
||||||
|
# Built application files
|
||||||
|
*.apk
|
||||||
|
*.ap_
|
||||||
|
*.aab
|
||||||
|
|
||||||
|
# Files for the ART/Dalvik VM
|
||||||
|
*.dex
|
||||||
|
|
||||||
|
# Java class files
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
bin/
|
||||||
|
gen/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Gradle files
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
.gradle/
|
||||||
/.idea/workspace.xml
|
build/
|
||||||
/.idea/libraries
|
|
||||||
/.idea/caches
|
# Signing files
|
||||||
.DS_Store
|
.signing/
|
||||||
/build
|
|
||||||
/captures
|
# Local configuration file (sdk path, etc)
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
# Proguard folder generated by Eclipse
|
||||||
|
proguard/
|
||||||
|
|
||||||
|
# Log Files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Android Studio
|
||||||
|
/*/build/
|
||||||
|
/*/local.properties
|
||||||
|
/*/out
|
||||||
|
/*/*/build
|
||||||
|
/*/*/production
|
||||||
|
captures/
|
||||||
|
.navigation/
|
||||||
|
*.ipr
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Keystore files
|
||||||
|
*.jks
|
||||||
|
*.keystore
|
||||||
|
|
||||||
|
# Google Services (e.g. APIs or Firebase)
|
||||||
|
# google-services.json
|
||||||
|
|
||||||
|
# Android Patch
|
||||||
|
gen-external-apklibs
|
||||||
|
|
||||||
|
# External native build folder generated in Android Studio 2.2 and later
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
/projectFilesBackup
|
|
||||||
|
# IntelliJ IDEA
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# User-specific configurations
|
||||||
|
.idea/caches/
|
||||||
|
.idea/libraries/
|
||||||
|
.idea/shelf/
|
||||||
|
.idea/workspace.xml
|
||||||
|
.idea/tasks.xml
|
||||||
|
.idea/.name
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/copyright/profiles_settings.xml
|
||||||
|
.idea/encodings.xml
|
||||||
|
.idea/misc.xml
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/scopes/scope_settings.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
.idea/vcs.xml
|
||||||
|
.idea/jsLibraryMappings.xml
|
||||||
|
.idea/datasources.xml
|
||||||
|
.idea/dataSources.ids
|
||||||
|
.idea/sqlDataSources.xml
|
||||||
|
.idea/dynamic.xml
|
||||||
|
.idea/uiDesigner.xml
|
||||||
|
.idea/assetWizardSettings.xml
|
||||||
|
.idea/gradle.xml
|
||||||
|
.idea/jarRepositorie
|
||||||
|
|||||||
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
20
.idea/gradle.xml
generated
20
.idea/gradle.xml
generated
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
||||||
<component name="GradleSettings">
|
|
||||||
<option name="linkedExternalProjectsSettings">
|
|
||||||
<GradleProjectSettings>
|
|
||||||
<option name="testRunner" value="PLATFORM" />
|
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
<option value="$PROJECT_DIR$/app" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
|
||||||
</GradleProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
48
.idea/misc.xml
generated
48
.idea/misc.xml
generated
@@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="NullableNotNullManager">
|
|
||||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
|
||||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
|
||||||
<option name="myNullables">
|
|
||||||
<value>
|
|
||||||
<list size="12">
|
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
|
||||||
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
|
||||||
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
|
||||||
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
|
||||||
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
|
|
||||||
<item index="6" class="java.lang.String" itemvalue="android.annotation.Nullable" />
|
|
||||||
<item index="7" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
|
|
||||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
|
|
||||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
|
|
||||||
<item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
|
|
||||||
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="myNotNulls">
|
|
||||||
<value>
|
|
||||||
<list size="11">
|
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
|
||||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
|
||||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
|
||||||
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
|
|
||||||
<item index="5" class="java.lang.String" itemvalue="android.annotation.NonNull" />
|
|
||||||
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
|
|
||||||
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
|
|
||||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
|
|
||||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
|
|
||||||
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectType">
|
|
||||||
<option name="id" value="Android" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
11
.idea/modules.xml
generated
11
.idea/modules.xml
generated
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Altas_-_Weather.iml" filepath="$PROJECT_DIR$/.idea/modules/Altas_-_Weather.iml" group="Altas_-_Weather" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Altas_-_Weather-app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Altas_-_Weather-app.iml" group="Altas_-_Weather/app" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/Weather_app.iml" filepath="$PROJECT_DIR$/Weather_app.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
12
.idea/runConfigurations.xml
generated
12
.idea/runConfigurations.xml
generated
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RunConfigurationProducerService">
|
|
||||||
<option name="ignoredProducers">
|
|
||||||
<set>
|
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -97,6 +97,7 @@ dependencies {
|
|||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
|
||||||
implementation 'androidx.navigation:navigation-ui-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'
|
||||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
|
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
|
||||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
||||||
// Unit testing
|
// Unit testing
|
||||||
@@ -108,8 +109,8 @@ dependencies {
|
|||||||
|
|
||||||
// android unit testing and espresso
|
// android unit testing and espresso
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
implementation 'androidx.test.espresso:espresso-core:3.2.0'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
|
androidTestImplementation 'androidx.test:rules:1.4.1-alpha06'
|
||||||
//mock websever for testing retrofit responses
|
//mock websever for testing retrofit responses
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
|
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
|
||||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
||||||
@@ -117,7 +118,6 @@ dependencies {
|
|||||||
//mockito and livedata testing
|
//mockito and livedata testing
|
||||||
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
||||||
implementation 'android.arch.core:core-testing'
|
implementation 'android.arch.core:core-testing'
|
||||||
androidTestImplementation 'androidx.test:rules:1.3.0-rc01'
|
|
||||||
|
|
||||||
// Mockk
|
// Mockk
|
||||||
def mockk_ver = "1.10.5"
|
def mockk_ver = "1.10.5"
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.application
|
package com.appttude.h_mal.atlas_weather.application
|
||||||
|
|
||||||
|
import androidx.room.Room
|
||||||
import androidx.test.espresso.IdlingRegistry
|
import androidx.test.espresso.IdlingRegistry
|
||||||
import androidx.test.espresso.idling.CountingIdlingResource
|
import androidx.test.espresso.idling.CountingIdlingResource
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.MockLocationProvider
|
import com.appttude.h_mal.atlas_weather.data.location.MockLocationProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.Api
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
|
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.MockingNetworkInterceptor
|
import com.appttude.h_mal.atlas_weather.data.network.interceptors.MockingNetworkInterceptor
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
|
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
|
||||||
|
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
||||||
|
import com.appttude.h_mal.atlas_weather.data.room.Converter
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
|
|
||||||
class TestAppClass : BaseAppClass() {
|
class TestAppClass : BaseAppClass() {
|
||||||
@@ -22,17 +23,23 @@ class TestAppClass : BaseAppClass() {
|
|||||||
IdlingRegistry.getInstance().register(idlingResources)
|
IdlingRegistry.getInstance().register(idlingResources)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createNetworkModule(): Api {
|
override fun createNetworkModule(): WeatherApi {
|
||||||
return NetworkModule().invoke<WeatherApi>(
|
return NetworkModule().invoke<WeatherApi>(
|
||||||
|
mockingNetworkInterceptor,
|
||||||
NetworkConnectionInterceptor(this),
|
NetworkConnectionInterceptor(this),
|
||||||
QueryParamsInterceptor(),
|
QueryParamsInterceptor(),
|
||||||
loggingInterceptor,
|
loggingInterceptor
|
||||||
mockingNetworkInterceptor
|
) as WeatherApi
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLocationModule() = MockLocationProvider()
|
override fun createLocationModule() = MockLocationProvider()
|
||||||
|
|
||||||
|
override fun createRoomDatabase(): AppDatabase {
|
||||||
|
return Room.inMemoryDatabaseBuilder(this, AppDatabase::class.java)
|
||||||
|
.addTypeConverter(Converter(this))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
fun stubUrl(url: String, rawPath: String) {
|
fun stubUrl(url: String, rawPath: String) {
|
||||||
val id = resources.getIdentifier(rawPath, "raw", packageName)
|
val id = resources.getIdentifier(rawPath, "raw", packageName)
|
||||||
val iStream = resources.openRawResource(id)
|
val iStream = resources.openRawResource(id)
|
||||||
@@ -40,7 +47,7 @@ class TestAppClass : BaseAppClass() {
|
|||||||
mockingNetworkInterceptor.addUrlStub(url = url, data = data)
|
mockingNetworkInterceptor.addUrlStub(url = url, data = data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeUrlStub(url: String){
|
fun removeUrlStub(url: String) {
|
||||||
mockingNetworkInterceptor.removeUrlStub(url = url)
|
mockingNetworkInterceptor.removeUrlStub(url = url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
|
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.action.ViewActions.pressBack
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import com.appttude.h_mal.atlas_weather.application.TestAppClass
|
import com.appttude.h_mal.atlas_weather.application.TestAppClass
|
||||||
@@ -8,7 +15,7 @@ import com.appttude.h_mal.atlas_weather.utils.Stubs
|
|||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
|
||||||
open class BaseTest() {
|
open class BaseTest {
|
||||||
|
|
||||||
lateinit var testApp: TestAppClass
|
lateinit var testApp: TestAppClass
|
||||||
|
|
||||||
@@ -21,6 +28,12 @@ open class BaseTest() {
|
|||||||
testApp = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
|
testApp = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
|
||||||
setupFeed()
|
setupFeed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun afterActivityLaunched() {
|
||||||
|
|
||||||
|
// Dismiss dialog
|
||||||
|
onView(withText("AGREE")).inRoot(isDialog()).check(matches(isDisplayed())).perform(ViewActions.click())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stubEndpoint(url: String, stub: Stubs) {
|
fun stubEndpoint(url: String, stub: Stubs) {
|
||||||
@@ -32,8 +45,7 @@ open class BaseTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {}
|
||||||
}
|
|
||||||
|
|
||||||
open fun setupFeed() {}
|
open fun setupFeed() {}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
|
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso
|
||||||
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions
|
||||||
|
import androidx.test.espresso.matcher.RootMatchers
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import androidx.test.rule.GrantPermissionRule
|
import androidx.test.rule.GrantPermissionRule
|
||||||
import com.appttude.h_mal.atlas_weather.monoWeather.robot.homeScreen
|
import com.appttude.h_mal.atlas_weather.monoWeather.robot.homeScreen
|
||||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.monoWeather.testsuite
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.test.core.app.ActivityScenario
|
||||||
|
import androidx.test.core.app.ActivityScenario.launch
|
||||||
|
import androidx.test.espresso.Espresso
|
||||||
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions
|
||||||
|
import androidx.test.espresso.matcher.RootMatchers
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
|
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
|
||||||
|
import androidx.test.rule.GrantPermissionRule
|
||||||
|
import com.appttude.h_mal.atlas_weather.application.TestAppClass
|
||||||
|
import com.appttude.h_mal.atlas_weather.monoWeather.robot.homeScreen
|
||||||
|
import com.appttude.h_mal.atlas_weather.monoWeather.ui.MainActivity
|
||||||
|
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4ClassRunner::class)
|
||||||
|
class HomePageUITestScenario : BaseMainScenario() {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
var mGrantPermissionRule: GrantPermissionRule =
|
||||||
|
GrantPermissionRule.grant(
|
||||||
|
"android.permission.ACCESS_COARSE_LOCATION")
|
||||||
|
|
||||||
|
override fun setupFeed() {
|
||||||
|
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Valid)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadApp_validWeatherResponse_returnsValidPage() {
|
||||||
|
homeScreen {
|
||||||
|
verifyCurrentTemperature(2)
|
||||||
|
verifyCurrentLocation("Mock Location")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class BaseMainScenario {
|
||||||
|
|
||||||
|
lateinit var scenario: ActivityScenario<MainActivity>
|
||||||
|
private lateinit var testApp : TestAppClass
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
scenario = launch(MainActivity::class.java)
|
||||||
|
scenario.moveToState(Lifecycle.State.INITIALIZED)
|
||||||
|
scenario.onActivity {
|
||||||
|
runBlocking {
|
||||||
|
testApp = it.application as TestAppClass
|
||||||
|
setupFeed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scenario.moveToState(Lifecycle.State.CREATED).onActivity {
|
||||||
|
Espresso.onView(ViewMatchers.withText("AGREE"))
|
||||||
|
.inRoot(RootMatchers.isDialog())
|
||||||
|
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
|
||||||
|
.perform(ViewActions.click())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stubEndpoint(url: String, stub: Stubs) {
|
||||||
|
testApp.stubUrl(url, stub.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unstubEndpoint(url: String) {
|
||||||
|
testApp.removeUrlStub(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {}
|
||||||
|
|
||||||
|
open fun setupFeed() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.monoWeather.ui.widget
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.test.espresso.Espresso
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
import androidx.test.rule.ActivityTestRule
|
||||||
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
|
||||||
|
class WidgetLocationPermissionActivityTest {
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
var mActivityTestRule : ActivityTestRule<WidgetLocationPermissionActivity> =
|
||||||
|
ActivityTestRule<WidgetLocationPermissionActivity>(WidgetLocationPermissionActivity::class.java, false, false)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun demo_test() {
|
||||||
|
val i = Intent()
|
||||||
|
i.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 112)
|
||||||
|
mActivityTestRule.launchActivity(i)
|
||||||
|
|
||||||
|
Espresso.onView((ViewMatchers.withId(R.id.declaration_text))).check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ abstract class BaseWidgetClass : AppWidgetProvider(){
|
|||||||
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||||
val idArray = intArrayOf(appWidgetId)
|
val idArray = intArrayOf(appWidgetId)
|
||||||
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
||||||
|
|
||||||
return PendingIntent.getBroadcast(
|
return PendingIntent.getBroadcast(
|
||||||
context, seconds, intentUpdate,
|
context, seconds, intentUpdate,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.application
|
package com.appttude.h_mal.atlas_weather.application
|
||||||
|
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.Api
|
import com.appttude.h_mal.atlas_weather.data.network.Api
|
||||||
@@ -26,14 +27,16 @@ const val LOCATION_PERMISSION_REQUEST = 505
|
|||||||
|
|
||||||
class AppClass : BaseAppClass() {
|
class AppClass : BaseAppClass() {
|
||||||
|
|
||||||
override fun createNetworkModule(): Api {
|
override fun createNetworkModule(): WeatherApi {
|
||||||
return NetworkModule().invoke<WeatherApi>(
|
return NetworkModule().invoke<WeatherApi>(
|
||||||
NetworkConnectionInterceptor(this),
|
NetworkConnectionInterceptor(this),
|
||||||
QueryParamsInterceptor(),
|
QueryParamsInterceptor(),
|
||||||
loggingInterceptor
|
loggingInterceptor
|
||||||
)
|
) as WeatherApi
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLocationModule() = LocationProviderImpl(this)
|
override fun createLocationModule() = LocationProviderImpl(this)
|
||||||
|
|
||||||
|
override fun createRoomDatabase(): AppDatabase = AppDatabase(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.application
|
package com.appttude.h_mal.atlas_weather.application
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.test.espresso.idling.CountingIdlingResource
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.Api
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||||
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
|
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
|
||||||
@@ -31,11 +24,11 @@ abstract class BaseAppClass : Application(), KodeinAware {
|
|||||||
override val kodein = Kodein.lazy {
|
override val kodein = Kodein.lazy {
|
||||||
import(androidXModule(this@BaseAppClass))
|
import(androidXModule(this@BaseAppClass))
|
||||||
|
|
||||||
bind() from singleton { createNetworkModule() as WeatherApi}
|
bind() from singleton { createNetworkModule() }
|
||||||
bind() from singleton { createLocationModule() }
|
bind() from singleton { createLocationModule() }
|
||||||
|
|
||||||
bind() from singleton { Gson() }
|
bind() from singleton { Gson() }
|
||||||
bind() from singleton { AppDatabase(instance()) }
|
bind() from singleton { createRoomDatabase() }
|
||||||
bind() from singleton { PreferenceProvider(instance()) }
|
bind() from singleton { PreferenceProvider(instance()) }
|
||||||
bind() from singleton { RepositoryImpl(instance(), instance(), instance()) }
|
bind() from singleton { RepositoryImpl(instance(), instance(), instance()) }
|
||||||
bind() from singleton { SettingsRepositoryImpl(instance()) }
|
bind() from singleton { SettingsRepositoryImpl(instance()) }
|
||||||
@@ -43,7 +36,8 @@ abstract class BaseAppClass : Application(), KodeinAware {
|
|||||||
bind() from provider { ApplicationViewModelFactory(instance(), instance()) }
|
bind() from provider { ApplicationViewModelFactory(instance(), instance()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun createNetworkModule() : Api
|
abstract fun createNetworkModule(): WeatherApi
|
||||||
abstract fun createLocationModule() : LocationProvider
|
abstract fun createLocationModule(): LocationProvider
|
||||||
|
abstract fun createRoomDatabase(): AppDatabase
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,19 +10,18 @@ import org.kodein.di.android.kodein
|
|||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
@ProvidedTypeConverter
|
@ProvidedTypeConverter
|
||||||
class Converter(context: Context): KodeinAware{
|
class Converter(context: Context) : KodeinAware {
|
||||||
override val kodein by kodein(context)
|
override val kodein by kodein(context)
|
||||||
private val gson by instance<Gson>()
|
private val gson by instance<Gson>()
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun fullWeatherToString(fullWeather: FullWeather): String{
|
fun fullWeatherToString(fullWeather: FullWeather): String {
|
||||||
return gson.toJson(fullWeather)
|
return gson.toJson(fullWeather)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun stringToFullWeather(string: String): FullWeather{
|
fun stringToFullWeather(string: String): FullWeather {
|
||||||
return gson.fromJson(string, FullWeather::class.java)
|
return gson.fromJson(string, FullWeather::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
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"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/forecast_listview"
|
android:id="@+id/forecast_listview"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
tools:listitem="@layout/db_list_item">
|
tools:listitem="@layout/db_list_item">
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,6 @@
|
|||||||
<string name="min">Min:</string>
|
<string name="min">Min:</string>
|
||||||
<string name="max">Max:</string>
|
<string name="max">Max:</string>
|
||||||
<string name="average">Average:</string>
|
<string name="average">Average:</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="ok">OK</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
android:name="com.appttude.h_mal.atlas_weather.monoWeather.ui.MainActivity"
|
android:name="com.appttude.h_mal.atlas_weather.monoWeather.ui.MainActivity"
|
||||||
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" />
|
||||||
@@ -30,13 +31,15 @@
|
|||||||
android:name="com.appttude.h_mal.atlas_weather.monoWeather.ui.settings.UnitSettingsActivity"
|
android:name="com.appttude.h_mal.atlas_weather.monoWeather.ui.settings.UnitSettingsActivity"
|
||||||
android:label="Settings" />
|
android:label="Settings" />
|
||||||
|
|
||||||
<activity android:name="com.appttude.h_mal.atlas_weather.monoWeather.ui.widget.WidgetLocationPermissionActivity">
|
<activity android:name="com.appttude.h_mal.atlas_weather.monoWeather.ui.widget.WidgetLocationPermissionActivity"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver android:name="com.appttude.h_mal.atlas_weather.monoWeather.widget.NewAppWidget">
|
<receiver android:name="com.appttude.h_mal.atlas_weather.monoWeather.widget.NewAppWidget"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
|
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
|
||||||
@@ -48,9 +51,6 @@
|
|||||||
android:resource="@xml/new_app_widget_info" />
|
android:resource="@xml/new_app_widget_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<service
|
|
||||||
android:name="com.appttude.h_mal.atlas_weather.monoWeather.widget.WidgetRemoteViewsService"
|
|
||||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
|
||||||
<service
|
<service
|
||||||
android:name="com.appttude.h_mal.atlas_weather.monoWeather.widget.WidgetJobServiceIntent"
|
android:name="com.appttude.h_mal.atlas_weather.monoWeather.widget.WidgetJobServiceIntent"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.monoWeather.dialog
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.Html
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
|
interface DeclarationBuilder{
|
||||||
|
val link: String
|
||||||
|
val message: String
|
||||||
|
|
||||||
|
fun Context.readFromResources(@StringRes id: Int) = resources.getString(id)
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.N)
|
||||||
|
fun buildMessage(): CharSequence? {
|
||||||
|
val link1 = "<font color='blue'><a href=\"$link\">here</a></font>"
|
||||||
|
val message = "$message See my privacy policy: $link1"
|
||||||
|
return Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.monoWeather.dialog
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionsDeclarationDialog(context: Context) : BaseDeclarationDialog(context) {
|
||||||
|
|
||||||
|
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."
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseDeclarationDialog(val context: Context): DeclarationBuilder {
|
||||||
|
abstract override val link: String
|
||||||
|
abstract override val message: String
|
||||||
|
|
||||||
|
fun showDialog(agreeCallback: () -> Unit = { }, disagreeCallback: () -> Unit = { Unit }) {
|
||||||
|
val myMessage = buildMessage()
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(context)
|
||||||
|
.setPositiveButton("agree") { _, _ ->
|
||||||
|
agreeCallback()
|
||||||
|
}
|
||||||
|
.setNegativeButton("disagree") { _, _ ->
|
||||||
|
disagreeCallback()
|
||||||
|
}
|
||||||
|
.setMessage(myMessage)
|
||||||
|
.setCancelable(false)
|
||||||
|
|
||||||
|
val alertDialog = builder.create()
|
||||||
|
alertDialog.show()
|
||||||
|
|
||||||
|
// Make the textview clickable. Must be called after show()
|
||||||
|
val msgTxt = alertDialog.findViewById<View>(android.R.id.message) as TextView?
|
||||||
|
msgTxt?.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,15 +2,18 @@ package com.appttude.h_mal.atlas_weather.monoWeather.ui
|
|||||||
|
|
||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Observer
|
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.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
|
||||||
@@ -24,7 +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
|
||||||
|
|
||||||
abstract class BaseFragment() : Fragment(), KodeinAware {
|
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId), KodeinAware {
|
||||||
|
|
||||||
override val kodein by kodein()
|
override val kodein by kodein()
|
||||||
val factory by instance<ApplicationViewModelFactory>()
|
val factory by instance<ApplicationViewModelFactory>()
|
||||||
@@ -119,4 +122,21 @@ abstract class BaseFragment() : Fragment(), KodeinAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
if (requestCode == LOCATION_PERMISSION_REQUEST) {
|
||||||
|
if (grantResults.isNotEmpty() &&
|
||||||
|
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsGranted()
|
||||||
|
displayToast("Permission granted")
|
||||||
|
} else {
|
||||||
|
permissionsRefused()
|
||||||
|
displayToast("Permission denied")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun permissionsGranted() {}
|
||||||
|
open fun permissionsRefused() {}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
|||||||
import kotlinx.android.synthetic.main.fragment_home.*
|
import kotlinx.android.synthetic.main.fragment_home.*
|
||||||
|
|
||||||
|
|
||||||
class WorldItemFragment : BaseFragment() {
|
class WorldItemFragment : BaseFragment(R.layout.fragment_home) {
|
||||||
|
|
||||||
private val viewModel by getFragmentViewModel<WorldViewModel>()
|
private val viewModel by getFragmentViewModel<WorldViewModel>()
|
||||||
private var param1: String? = null
|
private var param1: String? = null
|
||||||
@@ -24,12 +24,6 @@ class WorldItemFragment : BaseFragment() {
|
|||||||
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).locationName
|
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).locationName
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View? {
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home
|
package com.appttude.h_mal.atlas_weather.monoWeather.ui.home
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -9,9 +10,10 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
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.application.LOCATION_PERMISSION_REQUEST
|
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||||
|
import com.appttude.h_mal.atlas_weather.monoWeather.dialog.PermissionsDeclarationDialog
|
||||||
import com.appttude.h_mal.atlas_weather.monoWeather.ui.BaseFragment
|
import com.appttude.h_mal.atlas_weather.monoWeather.ui.BaseFragment
|
||||||
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.WeatherRecyclerAdapter
|
import com.appttude.h_mal.atlas_weather.monoWeather.ui.home.adapter.WeatherRecyclerAdapter
|
||||||
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
||||||
@@ -24,39 +26,29 @@ import kotlinx.android.synthetic.main.fragment_home.*
|
|||||||
* A simple [Fragment] subclass.
|
* A simple [Fragment] subclass.
|
||||||
* create an instance of this fragment.
|
* create an instance of this fragment.
|
||||||
*/
|
*/
|
||||||
class HomeFragment : BaseFragment() {
|
class HomeFragment : BaseFragment(R.layout.fragment_home) {
|
||||||
|
|
||||||
private val viewModel by getFragmentViewModel<MainViewModel>()
|
private val viewModel by getFragmentViewModel<MainViewModel>()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View? {
|
|
||||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val recyclerAdapter = WeatherRecyclerAdapter {
|
val recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
|
||||||
val directions =
|
navigateToFurtherDetails(it)
|
||||||
HomeFragmentDirections.actionHomeFragmentToFurtherDetailsFragment(it)
|
})
|
||||||
navigateTo(directions)
|
|
||||||
}
|
|
||||||
|
|
||||||
forecast_listview.apply {
|
forecast_listview.adapter = recyclerAdapter
|
||||||
layoutManager = LinearLayoutManager(context)
|
|
||||||
adapter = recyclerAdapter
|
|
||||||
}
|
|
||||||
|
|
||||||
|
PermissionsDeclarationDialog(requireContext()).showDialog(agreeCallback = {
|
||||||
getPermissionResult(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST){
|
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
|
||||||
viewModel.fetchData()
|
viewModel.fetchData()
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
swipe_refresh.apply {
|
swipe_refresh.apply {
|
||||||
setOnRefreshListener {
|
setOnRefreshListener {
|
||||||
getPermissionResult(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST){
|
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
|
||||||
viewModel.fetchData()
|
viewModel.fetchData()
|
||||||
isRefreshing = true
|
isRefreshing = true
|
||||||
}
|
}
|
||||||
@@ -73,16 +65,13 @@ class HomeFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
|
override fun permissionsGranted() {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
viewModel.fetchData()
|
||||||
if (requestCode == LOCATION_PERMISSION_REQUEST) {
|
}
|
||||||
if (grantResults.isNotEmpty()
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
private fun navigateToFurtherDetails(forecast: Forecast){
|
||||||
viewModel.fetchData()
|
val directions = HomeFragmentDirections
|
||||||
displayToast("Permission granted")
|
.actionHomeFragmentToFurtherDetailsFragment(forecast)
|
||||||
} else {
|
navigateTo(directions)
|
||||||
displayToast("Permission denied")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,20 +6,28 @@ import android.appwidget.AppWidgetManager.*
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat.checkSelfPermission
|
import androidx.core.app.ActivityCompat.checkSelfPermission
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
import com.appttude.h_mal.atlas_weather.monoWeather.dialog.DeclarationBuilder
|
||||||
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
||||||
import kotlinx.android.synthetic.monoWeather.permissions_declaration_dialog.*
|
import kotlinx.android.synthetic.monoWeather.permissions_declaration_dialog.*
|
||||||
|
|
||||||
const val PERMISSION_CODE = 401
|
const val PERMISSION_CODE = 401
|
||||||
|
|
||||||
class WidgetLocationPermissionActivity : AppCompatActivity() {
|
class WidgetLocationPermissionActivity : AppCompatActivity(), DeclarationBuilder {
|
||||||
|
override val link: String = "https://sites.google.com/view/hmaldev/home/monochrome"
|
||||||
|
override var message: String = ""
|
||||||
|
|
||||||
private var mAppWidgetId = INVALID_APPWIDGET_ID
|
private var mAppWidgetId = INVALID_APPWIDGET_ID
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
message = readFromResources(R.string.widget_declaration)
|
||||||
|
|
||||||
// Set the result to CANCELED. This will cause the widget host to cancel
|
// Set the result to CANCELED. This will cause the widget host to cancel
|
||||||
// out of the widget placement if the user presses the back button.
|
// out of the widget placement if the user presses the back button.
|
||||||
setResult(RESULT_CANCELED)
|
setResult(RESULT_CANCELED)
|
||||||
@@ -36,6 +44,10 @@ class WidgetLocationPermissionActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContentView(R.layout.permissions_declaration_dialog)
|
setContentView(R.layout.permissions_declaration_dialog)
|
||||||
|
findViewById<TextView>(R.id.declaration_text).apply {
|
||||||
|
text = buildMessage()
|
||||||
|
movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
}
|
||||||
|
|
||||||
submit.setOnClickListener {
|
submit.setOnClickListener {
|
||||||
if (checkSelfPermission(this, ACCESS_COARSE_LOCATION) != PERMISSION_GRANTED) {
|
if (checkSelfPermission(this, ACCESS_COARSE_LOCATION) != PERMISSION_GRANTED) {
|
||||||
@@ -44,6 +56,8 @@ class WidgetLocationPermissionActivity : AppCompatActivity() {
|
|||||||
submitWidget()
|
submitWidget()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancel.setOnClickListener { finish() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
|||||||
@@ -14,16 +14,10 @@ import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
|||||||
import kotlinx.android.synthetic.main.activity_add_forecast.*
|
import kotlinx.android.synthetic.main.activity_add_forecast.*
|
||||||
|
|
||||||
|
|
||||||
class AddLocationFragment : BaseFragment() {
|
class AddLocationFragment : BaseFragment(R.layout.activity_add_forecast) {
|
||||||
|
|
||||||
private val viewModel by getFragmentViewModel<WorldViewModel>()
|
private val viewModel by getFragmentViewModel<WorldViewModel>()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View? {
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
return inflater.inflate(R.layout.activity_add_forecast, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import kotlinx.android.synthetic.main.fragment_add_location.world_recycler
|
|||||||
* A simple [Fragment] subclass.
|
* A simple [Fragment] subclass.
|
||||||
* create an instance of this fragment.
|
* create an instance of this fragment.
|
||||||
*/
|
*/
|
||||||
class WorldFragment : BaseFragment() {
|
class WorldFragment : BaseFragment(R.layout.fragment__two) {
|
||||||
private val viewModel by getFragmentViewModel<WorldViewModel>()
|
private val viewModel by getFragmentViewModel<WorldViewModel>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -31,12 +31,6 @@ class WorldFragment : BaseFragment() {
|
|||||||
viewModel.fetchAllLocations()
|
viewModel.fetchAllLocations()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View? {
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
return inflater.inflate(R.layout.fragment__two, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ 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,
|
private val itemClick: (WeatherDisplay) -> Unit,
|
||||||
val itemLongClick: (String) -> Unit
|
private val itemLongClick: (String) -> Unit
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
var weather: MutableList<WeatherDisplay> = mutableListOf()
|
var weather: MutableList<WeatherDisplay> = mutableListOf()
|
||||||
|
|
||||||
@@ -78,23 +78,28 @@ class WorldRecyclerAdapter(
|
|||||||
return if (weather.size == 0) 1 else weather.size
|
return if (weather.size == 0) 1 else weather.size
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class WorldHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
|
internal class WorldHolderCurrent(cellView: View) : BaseViewHolder<WeatherDisplay>(cellView) {
|
||||||
|
|
||||||
var locationTV: TextView = listItemView.findViewById(R.id.db_location)
|
private val locationTV: TextView = cellView.findViewById(R.id.db_location)
|
||||||
var conditionTV: TextView = listItemView.findViewById(R.id.db_condition)
|
private val conditionTV: TextView = cellView.findViewById(R.id.db_condition)
|
||||||
var weatherIV: ImageView = listItemView.findViewById(R.id.db_icon)
|
private val weatherIV: ImageView = cellView.findViewById(R.id.db_icon)
|
||||||
var avgTempTV: TextView = listItemView.findViewById(R.id.db_main_temp)
|
private val avgTempTV: TextView = cellView.findViewById(R.id.db_main_temp)
|
||||||
var tempUnit: TextView = listItemView.findViewById(R.id.db_temp_unit)
|
private val tempUnit: TextView = cellView.findViewById(R.id.db_temp_unit)
|
||||||
|
|
||||||
fun bindData(weather: WeatherDisplay?){
|
override fun bindData(data: WeatherDisplay?){
|
||||||
locationTV.text = weather?.displayName
|
locationTV.text = data?.displayName
|
||||||
conditionTV.text = weather?.description
|
conditionTV.text = data?.description
|
||||||
weatherIV.loadImage(weather?.iconURL)
|
weatherIV.loadImage(data?.iconURL)
|
||||||
avgTempTV.text = weather?.forecast?.get(0)?.mainTemp
|
avgTempTV.text = data?.forecast?.get(0)?.mainTemp
|
||||||
tempUnit.text = itemView.context.getString(R.string.degrees)
|
tempUnit.text = itemView.context.getString(R.string.degrees)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class BaseViewHolder<T : Any>(cellView: View) : RecyclerView.ViewHolder(cellView) {
|
||||||
|
|
||||||
|
abstract fun bindData(data : T?)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,31 +2,28 @@ package com.appttude.h_mal.atlas_weather.monoWeather.widget
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
import android.app.TaskStackBuilder
|
import android.app.TaskStackBuilder
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.appwidget.AppWidgetProvider
|
import android.appwidget.AppWidgetProvider
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.core.app.JobIntentService
|
import androidx.core.app.JobIntentService
|
||||||
import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
|
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.KodeinAware
|
|
||||||
import org.kodein.di.LateInitKodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
abstract class BaseWidgetServiceIntentClass<T: AppWidgetProvider> : JobIntentService(){
|
abstract class BaseWidgetServiceIntentClass<T : AppWidgetProvider> : JobIntentService() {
|
||||||
|
|
||||||
lateinit var appWidgetManager: AppWidgetManager
|
lateinit var appWidgetManager: AppWidgetManager
|
||||||
lateinit var appWidgetIds: IntArray
|
lateinit var appWidgetIds: IntArray
|
||||||
|
|
||||||
fun initBaseWidget(componentName: ComponentName){
|
fun initBaseWidget(componentName: ComponentName) {
|
||||||
appWidgetManager = AppWidgetManager.getInstance(baseContext)
|
appWidgetManager = AppWidgetManager.getInstance(baseContext)
|
||||||
appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
|
appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
|
||||||
}
|
}
|
||||||
@@ -45,21 +42,24 @@ abstract class BaseWidgetServiceIntentClass<T: AppWidgetProvider> : JobIntentSer
|
|||||||
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||||
val idArray = intArrayOf(appWidgetId)
|
val idArray = intArrayOf(appWidgetId)
|
||||||
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray)
|
||||||
return PendingIntent.getBroadcast(
|
|
||||||
this, seconds, intentUpdate,
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
PendingIntent.getBroadcast(this, seconds, intentUpdate, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
} else {
|
||||||
|
PendingIntent.getBroadcast(this, seconds, intentUpdate, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a pending intent used to navigate to activity:
|
* create a pending intent used to navigate to activity:
|
||||||
* @param activityClass
|
* @param activityClass
|
||||||
*/
|
*/
|
||||||
fun <T: Activity> createClickingPendingIntent(activityClass: Class<T>): PendingIntent {
|
fun <T : Activity> createClickingPendingIntent(activityClass: Class<T>): PendingIntent {
|
||||||
val clickIntentTemplate = Intent(this, activityClass)
|
val clickIntentTemplate = Intent(this, activityClass)
|
||||||
|
|
||||||
return TaskStackBuilder.create(this)
|
return TaskStackBuilder.create(this)
|
||||||
.addNextIntentWithParentStack(clickIntentTemplate)
|
.addNextIntentWithParentStack(clickIntentTemplate)
|
||||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setImageView(
|
fun setImageView(
|
||||||
@@ -67,7 +67,7 @@ abstract class BaseWidgetServiceIntentClass<T: AppWidgetProvider> : JobIntentSer
|
|||||||
views: RemoteViews,
|
views: RemoteViews,
|
||||||
@IdRes viewId: Int,
|
@IdRes viewId: Int,
|
||||||
appWidgetId: Int
|
appWidgetId: Int
|
||||||
){
|
) {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
Picasso.get().load(path).into(views, viewId, intArrayOf(appWidgetId))
|
Picasso.get().load(path).into(views, viewId, intArrayOf(appWidgetId))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout 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_height="match_parent"
|
||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
android:paddingBottom="@dimen/activity_vertical_margin">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/imageView"
|
android:id="@+id/imageView"
|
||||||
@@ -19,33 +19,48 @@
|
|||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_bias="0.2"
|
app:layout_constraintVertical_bias="0.2"
|
||||||
app:srcCompat="@drawable/location_permission_decl" />
|
app:srcCompat="@drawable/cloud_symbol" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/declaration_text"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="64dp"
|
||||||
|
android:layout_marginRight="64dp"
|
||||||
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:layout_constraintTop_toBottomOf="@id/imageView"
|
app:layout_constraintTop_toBottomOf="@id/imageView"
|
||||||
android:layout_marginLeft="64dp"
|
|
||||||
android:layout_marginRight="64dp"
|
|
||||||
app:layout_constraintVertical_bias="0.2"
|
app:layout_constraintVertical_bias="0.2"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."/>
|
tools:text="@string/widget_declaration" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/submit"
|
android:id="@+id/submit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:backgroundTint="@android:color/white"
|
||||||
|
android:text="@string/ok"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.9"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_bias="0.85"
|
app:layout_constraintVertical_bias="0.85" />
|
||||||
app:layout_constraintHorizontal_bias="0.9"
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTint="@android:color/white"
|
android:backgroundTint="@android:color/white"
|
||||||
|
android:text="@string/cancel"
|
||||||
android:textColor="@android:color/black"
|
android:textColor="@android:color/black"
|
||||||
android:text="Ok" />
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.1"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.85" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -8,5 +8,6 @@
|
|||||||
|
|
||||||
<string name="title_home">Home</string>
|
<string name="title_home">Home</string>
|
||||||
<string name="title_world">World</string>
|
<string name="title_world">World</string>
|
||||||
<!-- TODO: Remove or change this placeholder text -->
|
|
||||||
|
<string name="widget_declaration">When using the app widget with app will require the use of background location services. Just to confirm the location data is used just to provide the end user with widget data. The use of background location permission is to update the widget at 30 minute intervals. </string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user