Merge branch 'master' into main_mono

# Conflicts:
#	.circleci/config.yml
This commit is contained in:
2023-08-13 18:35:25 +01:00
27 changed files with 285 additions and 205 deletions

View File

@@ -45,21 +45,19 @@ commands:
default: "AtlasWeather" default: "AtlasWeather"
package_suffix: package_suffix:
type: string type: string
default: "atlas_weather" default: "atlasWeather"
steps: steps:
- 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 --continue 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-target: ~/app-data
# store screenshots for failed ui tests # store screenshots for failed ui tests
- when: - when:
condition: on_fail condition: on_fail
steps: steps:
- store_artifacts: - store_artifacts:
path: ~/app-data path: app/build/outputs/connected_android_test_additional_output/monoWeatherDebugAndroidTest/connected
destination: screenshots destination: connected_android_test
# store test reports # store test reports
- store_artifacts: - store_artifacts:
path: app/build/reports/androidTests/connected path: app/build/reports/androidTests/connected
@@ -74,7 +72,6 @@ commands:
default: "AtlasWeather" default: "AtlasWeather"
steps: steps:
# The next step will run the unit tests # The next step will run the unit tests
- setup_repo
- android/decode-keystore: - android/decode-keystore:
keystore-location: "./app/keystore.jks" keystore-location: "./app/keystore.jks"
- run: - run:
@@ -84,6 +81,7 @@ commands:
- run: - run:
name: Run fastlane command to deploy to playstore name: Run fastlane command to deploy to playstore
command: | command: |
pwd
bundle exec fastlane deploy<< parameters.flavour >> bundle exec fastlane deploy<< parameters.flavour >>
- store_test_results: - store_test_results:
path: fastlane/report.xml path: fastlane/report.xml
@@ -97,9 +95,6 @@ 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:
@@ -109,10 +104,29 @@ jobs:
# See: https://circleci.com/docs/2.0/configuration-reference/#steps # See: https://circleci.com/docs/2.0/configuration-reference/#steps
steps: steps:
- setup_repo - setup_repo
# - run_tests: - run_tests:
# flavour: << parameters.flavour >> flavour: << parameters.flavour >>
# - run_ui_tests: run_instrumentation_test:
# flavour: << parameters.flavour >> # Parameters used for determining
parameters:
flavour:
type: string
default: "AtlasWeather"
package_suffix:
type: string
default: "atlasWeather"
# These next lines define the Android machine image executor.
# See: https://circleci.com/docs/2.0/executor-types/
executor:
name: android/android-machine
tag: 2023.05.1
# Add steps to the job
# See: https://circleci.com/docs/2.0/configuration-reference/#steps
steps:
- setup_repo
- run_ui_tests:
flavour: << parameters.flavour >>
package_suffix: << parameters.package_suffix >>
deploy-to-playstore: deploy-to-playstore:
parameters: parameters:
flavour: flavour:
@@ -137,11 +151,19 @@ workflows:
- build-and-test: - build-and-test:
context: appttude context: appttude
flavour: "MonoWeather" flavour: "MonoWeather"
package_suffix: "monoWeather"
filters: filters:
branches: branches:
ignore: ignore:
- main_atlas - main_atlas
- run_instrumentation_test:
context: appttude
flavour: "MonoWeather"
package_suffix: "monoWeather"
filters:
branches:
only:
- master
- main_mono
- deploy-to-playstore: - deploy-to-playstore:
context: appttude context: appttude
flavour: "MonoWeather" flavour: "MonoWeather"
@@ -156,11 +178,19 @@ 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:
- main_mono - main_mono
- run_instrumentation_test:
context: appttude
flavour: "AtlasWeather"
package_suffix: "atlasWeather"
filters:
branches:
only:
- master
- main_atlas
- deploy-to-playstore: - deploy-to-playstore:
context: appttude context: appttude
flavour: "AtlasWeather" flavour: "AtlasWeather"

2
.gitignore vendored
View File

@@ -91,7 +91,7 @@ gen-external-apklibs
.idea/jarRepositorie .idea/jarRepositorie
# Gem/fastlane # Gem/fastlane
/Gemfile.lock Gemfile.lock
/fastlane/report.xml /fastlane/report.xml
# Google play files # Google play files
/google-play-key.json /google-play-key.json

View File

@@ -5,13 +5,20 @@ plugins {
id 'kotlin-kapt' id 'kotlin-kapt'
id 'androidx.navigation.safeargs' id 'androidx.navigation.safeargs'
} }
def relStorePassword = System.getenv("RELEASE_STORE_PASSWORD")
def relKeyPassword = System.getenv("RELEASE_KEY_PASSWORD")
def relKeyAlias = System.getenv("RELEASE_KEY_ALIAS")
def keystorePath = System.getenv('PWD') + "/app/keystore.jks"
def keystore = file(keystorePath).exists() ? file(keystorePath) : null
android { android {
lintOptions { lintOptions {
abortOnError false abortOnError false
} }
compileSdkVersion 33
defaultConfig { defaultConfig {
applicationId "com.appttude.h_mal.atlas_weather" applicationId "com.appttude.h_mal.atlas_weather"
compileSdk 33
minSdkVersion 26 minSdkVersion 26
targetSdkVersion 33 targetSdkVersion 33
versionCode 5 versionCode 5
@@ -40,6 +47,14 @@ android {
} }
} }
signingConfigs {
release {
storePassword relStorePassword
keyPassword relKeyPassword
keyAlias relKeyAlias
storeFile keystore
}
}
testOptions { testOptions {
unitTests { unitTests {
includeAndroidResources = true includeAndroidResources = true
@@ -48,6 +63,7 @@ android {
} }
buildTypes { buildTypes {
release { release {
signingConfig signingConfigs.release
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
@@ -99,6 +115,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1' 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.fragment:fragment:1.2.0'
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'
@@ -187,4 +204,8 @@ dependencies {
/ * screenshot library * / / * screenshot library * /
androidTestImplementation 'tools.fastlane:screengrab:2.1.1' androidTestImplementation 'tools.fastlane:screengrab:2.1.1'
/ * Permissions dispatcher * /
def dispatcher_ver = "4.9.2"
implementation "com.github.permissions-dispatcher:permissionsdispatcher:${dispatcher_ver}"
kapt "com.github.permissions-dispatcher:permissionsdispatcher-processor:${dispatcher_ver}"
} }

View File

@@ -1,20 +0,0 @@
package com.appttude.h_mal.monoWeather
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 com.appttude.h_mal.atlas_weather.BaseTest
import com.appttude.h_mal.atlas_weather.ui.MainActivity
open class MonoBaseTest : BaseTest<MainActivity>(MainActivity::class.java) {
override fun afterLaunch() {
// Dismiss dialog on start up
Espresso.onView(ViewMatchers.withText("AGREE"))
.inRoot(RootMatchers.isDialog())
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
.perform(ViewActions.click())
}
}

View File

@@ -1,12 +1,13 @@
package com.appttude.h_mal.monoWeather.tests package com.appttude.h_mal.monoWeather.tests
import com.appttude.h_mal.atlas_weather.BaseTest
import com.appttude.h_mal.atlas_weather.ui.MainActivity
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.robot.weatherScreen import com.appttude.h_mal.monoWeather.robot.weatherScreen
import org.junit.Test import org.junit.Test
class HomePageNoDataUITest : MonoBaseTest() { class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
override fun beforeLaunch() { override fun beforeLaunch() {
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.InvalidKey, 400) stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.InvalidKey, 400)

View File

@@ -1,12 +1,13 @@
package com.appttude.h_mal.monoWeather.tests package com.appttude.h_mal.monoWeather.tests
import com.appttude.h_mal.atlas_weather.BaseTest
import com.appttude.h_mal.atlas_weather.ui.MainActivity
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.robot.weatherScreen import com.appttude.h_mal.monoWeather.robot.weatherScreen
import org.junit.Test import org.junit.Test
class HomePageUITest : MonoBaseTest() { class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
override fun beforeLaunch() { override fun beforeLaunch() {
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric) stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)

View File

@@ -1,8 +1,9 @@
package com.appttude.h_mal.monoWeather.tests package com.appttude.h_mal.monoWeather.tests
import com.appttude.h_mal.atlas_weather.BaseTest
import com.appttude.h_mal.atlas_weather.ui.MainActivity
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.robot.ContainerRobot.Tab.WORLD import com.appttude.h_mal.monoWeather.robot.ContainerRobot.Tab.WORLD
import com.appttude.h_mal.monoWeather.robot.addLocation import com.appttude.h_mal.monoWeather.robot.addLocation
import com.appttude.h_mal.monoWeather.robot.container import com.appttude.h_mal.monoWeather.robot.container
@@ -10,7 +11,7 @@ import com.appttude.h_mal.monoWeather.robot.weatherScreen
import com.appttude.h_mal.monoWeather.robot.world import com.appttude.h_mal.monoWeather.robot.world
import org.junit.Test import org.junit.Test
class WorldPageUITest : MonoBaseTest() { class WorldPageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
override fun beforeLaunch() { override fun beforeLaunch() {
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric) stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)

View File

@@ -13,21 +13,31 @@ import androidx.navigation.ui.onNavDestinationSelected
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.application.LOCATION_PERMISSION_REQUEST import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
import com.appttude.h_mal.atlas_weather.base.BaseFragment
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.ui.BaseFragment import com.appttude.h_mal.atlas_weather.ui.dialog.PermissionsDeclarationDialog
import com.appttude.h_mal.atlas_weather.ui.home.adapter.WeatherRecyclerAdapter import com.appttude.h_mal.atlas_weather.ui.home.adapter.WeatherRecyclerAdapter
import com.appttude.h_mal.atlas_weather.utils.displayToast
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.MainViewModel import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.*
import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.OnNeverAskAgain
import permissions.dispatcher.OnPermissionDenied
import permissions.dispatcher.OnShowRationale
import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
/** /**
* A simple [Fragment] subclass. * A simple [Fragment] subclass.
* create an instance of this fragment. * create an instance of this fragment.
*/ */
@RuntimePermissions
class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) { class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
private lateinit var recyclerAdapter: WeatherRecyclerAdapter
lateinit var recyclerAdapter: WeatherRecyclerAdapter
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -36,35 +46,28 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
swipe_refresh.apply { swipe_refresh.apply {
setOnRefreshListener { setOnRefreshListener {
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) { showLocationWithPermissionCheck()
viewModel.fetchData()
isRefreshing = true isRefreshing = true
} }
} }
}
recyclerAdapter = WeatherRecyclerAdapter(itemClick = { recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
navigateToFurtherDetails(it) navigateToFurtherDetails(it)
}) })
forecast_listview.apply { forecast_listview.adapter = recyclerAdapter
layoutManager = LinearLayoutManager(context)
adapter = recyclerAdapter
}
} }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
showLocationWithPermissionCheck()
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
viewModel.fetchData()
}
} }
override fun onSuccess(data: Any?) { override fun onSuccess(data: Any?) {
super.onSuccess(data) super.onSuccess(data)
swipe_refresh.isRefreshing = false swipe_refresh.isRefreshing = false
if (data is WeatherDisplay) { if (data is WeatherDisplay) {
recyclerAdapter.addCurrent(data) recyclerAdapter.addCurrent(data)
} }
@@ -75,14 +78,8 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
swipe_refresh.isRefreshing = false swipe_refresh.isRefreshing = false
} }
@SuppressLint("MissingPermission")
override fun permissionsGranted() {
viewModel.fetchData()
}
private fun navigateToFurtherDetails(forecast: Forecast) { private fun navigateToFurtherDetails(forecast: Forecast) {
val directions = HomeFragmentDirections val directions = HomeFragmentDirections.actionHomeFragmentToFurtherDetailsFragment(forecast)
.actionHomeFragmentToFurtherDetailsFragment(forecast)
navigateTo(directions) navigateTo(directions)
} }
@@ -95,4 +92,35 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
val navController = findNavController(requireActivity(), R.id.container) val navController = findNavController(requireActivity(), R.id.container)
return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item) return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item)
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated method
onRequestPermissionsResult(requestCode, grantResults)
}
@SuppressLint("MissingPermission")
@NeedsPermission(ACCESS_COARSE_LOCATION)
fun showLocation() {
viewModel.fetchData()
}
@OnShowRationale(ACCESS_COARSE_LOCATION)
fun showRationaleForLocation(request: PermissionRequest) {
PermissionsDeclarationDialog(requireContext()).showDialog({
request.proceed()
}, {
request.cancel()
})
}
@OnPermissionDenied(ACCESS_COARSE_LOCATION)
fun onLocationDenied() {
displayToast("Location permissions have been denied")
}
@OnNeverAskAgain(ACCESS_COARSE_LOCATION)
fun onLocationNeverAskAgain() {
displayToast("Location permissions have been to never ask again")
}
} }

View File

@@ -3,7 +3,7 @@ package com.appttude.h_mal.atlas_weather.ui.world
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import com.appttude.h_mal.atlas_weather.R import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.BaseFragment import com.appttude.h_mal.atlas_weather.base.BaseFragment
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.goBack import com.appttude.h_mal.atlas_weather.utils.goBack
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel

View File

@@ -5,8 +5,8 @@ import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
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.base.BaseFragment
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
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.WorldViewModel import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import kotlinx.android.synthetic.atlasWeather.fragment_add_location.floatingActionButton import kotlinx.android.synthetic.atlasWeather.fragment_add_location.floatingActionButton

View File

@@ -1,29 +1,22 @@
package com.appttude.h_mal.atlas_weather.base package com.appttude.h_mal.atlas_weather.base
import android.content.Intent import android.content.Intent
import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup.LayoutParams import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.inflate import android.view.ViewGroup.inflate
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelLazy
import com.appttude.h_mal.atlas_weather.R import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.helper.GenericsHelper.getGenericClassAt
import com.appttude.h_mal.atlas_weather.model.ViewState
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.utils.triggerAnimation import com.appttude.h_mal.atlas_weather.utils.triggerAnimation
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.baseViewModels.BaseViewModel
import org.kodein.di.KodeinAware import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein import org.kodein.di.android.kodein
import org.kodein.di.generic.instance
abstract class BaseActivity : AppCompatActivity(), KodeinAware { abstract class BaseActivity : AppCompatActivity(), KodeinAware {
private lateinit var loadingView: View private var loadingView: View? = null
override val kodein by kodein() override val kodein by kodein()
@@ -36,9 +29,9 @@ abstract class BaseActivity : AppCompatActivity(), KodeinAware {
*/ */
private fun instantiateLoadingView() { private fun instantiateLoadingView() {
loadingView = inflate(this, R.layout.progress_layout, null) loadingView = inflate(this, R.layout.progress_layout, null)
loadingView.setOnClickListener(null) loadingView?.setOnClickListener(null)
addContentView(loadingView, LayoutParams(MATCH_PARENT, MATCH_PARENT)) addContentView(loadingView, LayoutParams(MATCH_PARENT, MATCH_PARENT))
loadingView.hide() loadingView?.hide()
} }
override fun onStart() { override fun onStart() {
@@ -55,14 +48,14 @@ abstract class BaseActivity : AppCompatActivity(), KodeinAware {
* Called in case of success or some data emitted from the liveData in viewModel * Called in case of success or some data emitted from the liveData in viewModel
*/ */
open fun onStarted() { open fun onStarted() {
loadingView.fadeIn() loadingView?.fadeIn()
} }
/** /**
* Called in case of success or some data emitted from the liveData in viewModel * Called in case of success or some data emitted from the liveData in viewModel
*/ */
open fun onSuccess(data: Any?) { open fun onSuccess(data: Any?) {
loadingView.fadeOut() loadingView?.fadeOut()
} }
/** /**
@@ -70,7 +63,7 @@ abstract class BaseActivity : AppCompatActivity(), KodeinAware {
*/ */
open fun onFailure(error: Any?) { open fun onFailure(error: Any?) {
if (error is String) displayToast(error) if (error is String) displayToast(error)
loadingView.fadeOut() loadingView?.fadeOut()
} }
private fun View.fadeIn() = apply { private fun View.fadeIn() = apply {
@@ -85,7 +78,7 @@ abstract class BaseActivity : AppCompatActivity(), KodeinAware {
override fun onBackPressed() { override fun onBackPressed() {
loadingView.hide() loadingView?.hide()
super.onBackPressed() super.onBackPressed()
} }
} }

View File

@@ -1,26 +1,14 @@
package com.appttude.h_mal.atlas_weather.ui package com.appttude.h_mal.atlas_weather.base
import android.annotation.SuppressLint
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.annotation.LayoutRes
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.createViewModelLazy import androidx.fragment.app.createViewModelLazy
import androidx.lifecycle.Observer import com.appttude.h_mal.atlas_weather.base.baseViewModels.BaseViewModel
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.appttude.h_mal.atlas_weather.application.LOCATION_PERMISSION_REQUEST
import com.appttude.h_mal.atlas_weather.base.BaseActivity
import com.appttude.h_mal.atlas_weather.helper.GenericsHelper.getGenericClassAt import com.appttude.h_mal.atlas_weather.helper.GenericsHelper.getGenericClassAt
import com.appttude.h_mal.atlas_weather.model.ViewState import com.appttude.h_mal.atlas_weather.model.ViewState
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.viewmodel.ApplicationViewModelFactory import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.baseViewModels.BaseViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.kodein.di.KodeinAware import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
@@ -32,7 +20,7 @@ abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int)
KodeinAware { KodeinAware {
override val kodein by kodein() override val kodein by kodein()
val factory by instance<ApplicationViewModelFactory>() private val factory by instance<ApplicationViewModelFactory>()
val viewModel: V by getFragmentViewModel() val viewModel: V by getFragmentViewModel()
@@ -47,7 +35,6 @@ abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int)
shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime) shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
} }
@Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
mActivity = activity as BaseActivity mActivity = activity as BaseActivity
@@ -84,53 +71,4 @@ abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int)
open fun onFailure(error: Any?) { open fun onFailure(error: Any?) {
mActivity?.onFailure(error) mActivity?.onFailure(error)
} }
/**
* Request a permission for
* @param permission with
* @param permissionCode
* Callback if is already permission granted
* @param permissionGranted
*/
fun getPermissionResult(
permission: String,
permissionCode: Int,
permissionGranted: () -> Unit
) {
if (ActivityCompat.checkSelfPermission(
requireContext(),
permission
) != PackageManager.PERMISSION_GRANTED
) {
requestPermissions(arrayOf(permission), permissionCode)
return
} else {
CoroutineScope(Dispatchers.Main).launch {
permissionGranted.invoke()
}
}
}
@SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == LOCATION_PERMISSION_REQUEST) {
if (grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
permissionsGranted()
displayToast("Permission granted")
} else {
permissionsRefused()
displayToast("Permission denied")
}
}
}
open fun permissionsGranted() {}
open fun permissionsRefused() {}
} }

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.atlas_weather.viewmodel.baseViewModels package com.appttude.h_mal.atlas_weather.base.baseViewModels
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData

View File

@@ -1,6 +1,5 @@
package com.appttude.h_mal.atlas_weather.viewmodel.baseViewModels package com.appttude.h_mal.atlas_weather.base.baseViewModels
import androidx.lifecycle.ViewModel
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather import com.appttude.h_mal.atlas_weather.model.weather.FullWeather

View File

@@ -44,6 +44,8 @@ class PreferenceProvider(
preference.edit().putBoolean("FIRST_TIME_RUN", false).apply() preference.edit().putBoolean("FIRST_TIME_RUN", false).apply()
} }
fun getFirstTimeRun() = preference.getBoolean("FIRST_TIME_RUN", false)
fun isWidgetBackground(): Boolean { fun isWidgetBackground(): Boolean {
return preference.getBoolean("widget_black_background", false) return preference.getBoolean("widget_black_background", false)
} }

View File

@@ -3,5 +3,6 @@ package com.appttude.h_mal.atlas_weather.data.repository
interface SettingsRepository { interface SettingsRepository {
fun isNotificationsEnabled(): Boolean fun isNotificationsEnabled(): Boolean
fun setFirstTime() fun setFirstTime()
fun getFirstTime(): Boolean
fun isBlackBackground(): Boolean fun isBlackBackground(): Boolean
} }

View File

@@ -9,6 +9,7 @@ class SettingsRepositoryImpl(
override fun isNotificationsEnabled(): Boolean = prefs.isNotificationsEnabled() override fun isNotificationsEnabled(): Boolean = prefs.isNotificationsEnabled()
override fun setFirstTime() = prefs.setFirstTimeRun() override fun setFirstTime() = prefs.setFirstTimeRun()
override fun getFirstTime(): Boolean = prefs.getFirstTimeRun()
override fun isBlackBackground() = prefs.isWidgetBackground() override fun isBlackBackground() = prefs.isWidgetBackground()
} }

View File

@@ -7,7 +7,7 @@ import com.appttude.h_mal.atlas_weather.data.repository.Repository
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.viewmodel.baseViewModels.WeatherViewModel import com.appttude.h_mal.atlas_weather.base.baseViewModels.WeatherViewModel
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@@ -6,7 +6,7 @@ import com.appttude.h_mal.atlas_weather.data.repository.Repository
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.model.types.LocationType import com.appttude.h_mal.atlas_weather.model.types.LocationType
import com.appttude.h_mal.atlas_weather.viewmodel.baseViewModels.WeatherViewModel import com.appttude.h_mal.atlas_weather.base.baseViewModels.WeatherViewModel
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@@ -6,7 +6,7 @@ import android.view.View
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.model.forecast.WeatherDisplay import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.ui.BaseFragment import com.appttude.h_mal.atlas_weather.base.BaseFragment
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.WorldViewModel import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import com.appttude.h_mal.monoWeather.ui.home.adapter.WeatherRecyclerAdapter import com.appttude.h_mal.monoWeather.ui.home.adapter.WeatherRecyclerAdapter

View File

@@ -11,24 +11,30 @@ import androidx.fragment.app.Fragment
import androidx.navigation.Navigation.findNavController import androidx.navigation.Navigation.findNavController
import androidx.navigation.ui.onNavDestinationSelected import androidx.navigation.ui.onNavDestinationSelected
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.base.BaseFragment
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.utils.displayToast
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.MainViewModel import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
import com.appttude.h_mal.monoWeather.dialog.PermissionsDeclarationDialog import com.appttude.h_mal.monoWeather.dialog.PermissionsDeclarationDialog
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
import com.appttude.h_mal.monoWeather.ui.home.adapter.WeatherRecyclerAdapter import com.appttude.h_mal.monoWeather.ui.home.adapter.WeatherRecyclerAdapter
import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.*
import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.OnNeverAskAgain
import permissions.dispatcher.OnPermissionDenied
import permissions.dispatcher.OnShowRationale
import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
/** /**
* A simple [Fragment] subclass. * A simple [Fragment] subclass.
* create an instance of this fragment. * create an instance of this fragment.
*/ */
@RuntimePermissions
class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) { class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
lateinit var dialog: PermissionsDeclarationDialog
lateinit var recyclerAdapter: WeatherRecyclerAdapter lateinit var recyclerAdapter: WeatherRecyclerAdapter
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
@@ -36,16 +42,12 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true) setHasOptionsMenu(true)
dialog = PermissionsDeclarationDialog(requireContext())
swipe_refresh.apply { swipe_refresh.apply {
setOnRefreshListener { setOnRefreshListener {
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) { showLocationWithPermissionCheck()
viewModel.fetchData()
isRefreshing = true isRefreshing = true
} }
} }
}
recyclerAdapter = WeatherRecyclerAdapter(itemClick = { recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
navigateToFurtherDetails(it) navigateToFurtherDetails(it)
@@ -57,16 +59,7 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
dialog.showDialog(agreeCallback = { showLocationWithPermissionCheck()
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
viewModel.fetchData()
}
})
}
override fun onStop() {
super.onStop()
dialog.dismiss()
} }
override fun onSuccess(data: Any?) { override fun onSuccess(data: Any?) {
@@ -83,14 +76,8 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
swipe_refresh.isRefreshing = false swipe_refresh.isRefreshing = false
} }
@SuppressLint("MissingPermission")
override fun permissionsGranted() {
viewModel.fetchData()
}
private fun navigateToFurtherDetails(forecast: Forecast) { private fun navigateToFurtherDetails(forecast: Forecast) {
val directions = HomeFragmentDirections val directions = HomeFragmentDirections.actionHomeFragmentToFurtherDetailsFragment(forecast)
.actionHomeFragmentToFurtherDetailsFragment(forecast)
navigateTo(directions) navigateTo(directions)
} }
@@ -103,4 +90,35 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
val navController = findNavController(requireActivity(), R.id.container) val navController = findNavController(requireActivity(), R.id.container)
return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item) return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item)
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated method
onRequestPermissionsResult(requestCode, grantResults)
}
@SuppressLint("MissingPermission")
@NeedsPermission(ACCESS_COARSE_LOCATION)
fun showLocation() {
viewModel.fetchData()
}
@OnShowRationale(ACCESS_COARSE_LOCATION)
fun showRationaleForLocation(request: PermissionRequest) {
PermissionsDeclarationDialog(requireContext()).showDialog({
request.proceed()
}, {
request.cancel()
})
}
@OnPermissionDenied(ACCESS_COARSE_LOCATION)
fun onLocationDenied() {
displayToast("Location permissions have been denied")
}
@OnNeverAskAgain(ACCESS_COARSE_LOCATION)
fun onLocationNeverAskAgain() {
displayToast("Location permissions have been to never ask again")
}
} }

View File

@@ -1,5 +1,6 @@
package com.appttude.h_mal.monoWeather.ui.widget package com.appttude.h_mal.monoWeather.ui.widget
import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.app.Activity import android.app.Activity
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
@@ -7,20 +8,26 @@ import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.widget.TextView import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
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.utils.displayToast import com.appttude.h_mal.atlas_weather.utils.displayToast
import com.appttude.h_mal.monoWeather.dialog.DeclarationBuilder import com.appttude.h_mal.monoWeather.dialog.DeclarationBuilder
import com.appttude.h_mal.monoWeather.dialog.PermissionsDeclarationDialog
import kotlinx.android.synthetic.monoWeather.permissions_declaration_dialog.cancel import kotlinx.android.synthetic.monoWeather.permissions_declaration_dialog.cancel
import kotlinx.android.synthetic.monoWeather.permissions_declaration_dialog.submit import kotlinx.android.synthetic.monoWeather.permissions_declaration_dialog.submit
import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.OnNeverAskAgain
import permissions.dispatcher.OnPermissionDenied
import permissions.dispatcher.OnShowRationale
import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
const val PERMISSION_CODE = 401 @RuntimePermissions
class WidgetLocationPermissionActivity : AppCompatActivity(), DeclarationBuilder { class WidgetLocationPermissionActivity : AppCompatActivity(), DeclarationBuilder {
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 var message: String = "" override var message: String = ""
@@ -54,31 +61,16 @@ class WidgetLocationPermissionActivity : AppCompatActivity(), DeclarationBuilder
} }
submit.setOnClickListener { submit.setOnClickListener {
if (checkSelfPermission(this, ACCESS_COARSE_LOCATION) != PERMISSION_GRANTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
requestPermissions(arrayOf(ACCESS_COARSE_LOCATION), PERMISSION_CODE) showBackgroundLocationWithPermissionCheck()
} else { } else {
submitWidget() showLocationWithPermissionCheck()
} }
} }
cancel.setOnClickListener { finish() } cancel.setOnClickListener { finish() }
} }
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PERMISSION_GRANTED) {
submitWidget()
} else {
displayToast("Location Permission denied")
}
}
}
private fun submitWidget() { private fun submitWidget() {
sendUpdateIntent() sendUpdateIntent()
finishCurrencyWidgetActivity() finishCurrencyWidgetActivity()
@@ -86,8 +78,7 @@ class WidgetLocationPermissionActivity : AppCompatActivity(), DeclarationBuilder
private fun finishCurrencyWidgetActivity() { private fun finishCurrencyWidgetActivity() {
// Make sure we pass back the original appWidgetId // Make sure we pass back the original appWidgetId
val resultValue = intent val resultValue = Intent().putExtra(EXTRA_APPWIDGET_ID, mAppWidgetId)
resultValue.putExtra(EXTRA_APPWIDGET_ID, mAppWidgetId)
setResult(Activity.RESULT_OK, resultValue) setResult(Activity.RESULT_OK, resultValue)
finish() finish()
} }
@@ -106,4 +97,66 @@ class WidgetLocationPermissionActivity : AppCompatActivity(), DeclarationBuilder
sendBroadcast(this) sendBroadcast(this)
} }
} }
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated method
onRequestPermissionsResult(requestCode, grantResults)
}
@NeedsPermission(ACCESS_COARSE_LOCATION)
fun showLocation() {
submitWidget()
}
@RequiresApi(Build.VERSION_CODES.Q)
@NeedsPermission(ACCESS_BACKGROUND_LOCATION)
fun showBackgroundLocation() {
submitWidget()
}
@OnShowRationale(ACCESS_COARSE_LOCATION)
fun showRationaleForLocation(request: PermissionRequest) {
PermissionsDeclarationDialog(this).showDialog({
request.proceed()
}, {
request.cancel()
})
}
@RequiresApi(Build.VERSION_CODES.Q)
@OnShowRationale(ACCESS_BACKGROUND_LOCATION)
fun showRationaleForBackgroundLocation(request: PermissionRequest) {
PermissionsDeclarationDialog(this).showDialog({
request.proceed()
}, {
request.cancel()
})
}
@OnPermissionDenied(ACCESS_COARSE_LOCATION)
fun onLocationDenied() {
displayToast("Location permissions have been denied")
}
@RequiresApi(Build.VERSION_CODES.Q)
@OnPermissionDenied(ACCESS_BACKGROUND_LOCATION)
fun onBackgroundLocationDenied() {
displayToast("Background Location permissions have been denied")
}
@OnNeverAskAgain(ACCESS_COARSE_LOCATION)
fun onLocationNeverAskAgain() {
displayToast("Location permissions have been to never ask again")
}
@RequiresApi(Build.VERSION_CODES.Q)
@OnNeverAskAgain(ACCESS_BACKGROUND_LOCATION)
fun onBackgroundLocationNeverAskAgain() {
displayToast("Background Location permissions have been to never ask again")
}
} }

View File

@@ -3,7 +3,7 @@ package com.appttude.h_mal.monoWeather.ui.world
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import com.appttude.h_mal.atlas_weather.R import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.BaseFragment import com.appttude.h_mal.atlas_weather.base.BaseFragment
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.goBack import com.appttude.h_mal.atlas_weather.utils.goBack
import com.appttude.h_mal.atlas_weather.utils.hideKeyboard import com.appttude.h_mal.atlas_weather.utils.hideKeyboard

View File

@@ -6,7 +6,7 @@ import androidx.fragment.app.Fragment
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.model.forecast.WeatherDisplay import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
import com.appttude.h_mal.atlas_weather.ui.BaseFragment import com.appttude.h_mal.atlas_weather.base.BaseFragment
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.WorldViewModel import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import com.appttude.h_mal.monoWeather.ui.world.WorldFragmentDirections.actionWorldFragmentToWorldItemFragment import com.appttude.h_mal.monoWeather.ui.world.WorldFragmentDirections.actionWorldFragmentToWorldItemFragment

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:configure="com.appttude.h_mal.monoWeather.ui.widget.WidgetLocationPermissionActivity"
android:initialKeyguardLayout="@layout/weather_app_widget"
android:initialLayout="@layout/weather_app_widget"
android:targetCellWidth="5"
android:targetCellHeight="1"
android:previewImage="@drawable/widget_screenshot"
android:resizeMode="vertical|horizontal"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen">
</appwidget-provider>

0
gradlew vendored Normal file → Executable file
View File