- Fastlane completed

- Circleci config completed
 - Flavours build completed
This commit is contained in:
2023-08-07 15:01:51 +01:00
parent 377970a3fc
commit 03ee1dc249
29 changed files with 343 additions and 237 deletions

View File

@@ -15,9 +15,15 @@ commands:
steps:
- checkout
- android/restore-gradle-cache
build_gradle:
description: Build the gradle
steps:
- android/restore-gradle-cache
- run:
name: Download Dependencies
command: ./gradlew androidDependencies
command: |
sudo chmod +x ./gradlew
./gradlew androidDependencies
- android/save-gradle-cache
run_tests:
description: run tests for flavour specified
@@ -26,14 +32,17 @@ commands:
type: string
default: "AtlasWeather"
steps:
# The next step will run the unit tests
- android/run-tests:
test-command: ./gradlew test<< parameters.flavour >>DebugUnitTest --continue
- store_artifacts:
path: app/build/reports
destination: reports
- store_test_results:
path: app/build/test-results
# The next step will run the unit tests
- build_gradle
- run:
name: Run non-instrumentation unit tests
command: |
./gradlew test<< parameters.flavour >>DebugUnitTest --continue
- store_artifacts:
path: app/build/reports
destination: reports
- store_test_results:
path: app/build/test-results
run_ui_tests:
description: run tests for flavour specified
parameters:
@@ -41,27 +50,44 @@ commands:
type: string
default: "AtlasWeather"
steps:
- android/start-emulator-and-run-tests:
post-emulator-launch-assemble-command: ./gradlew assemble<< parameters.flavour >>DebugAndroidTest
test-command: ./gradlew connected<< parameters.flavour >>DebugAndroidTest
system-image: system-images;android-25;google_apis;x86
max-tries: 1
kill-emulators: false
- run:
name: Pull screenshots from device
command: |
mkdir ~/screenshots
adb pull /storage/emulated/0/Android/data/com.appttude.h_mal.atlas_weather/files/screengrab/en-US/images/screenshots ~/screenshots
when: on_fail
# store test reports
- store_artifacts:
path: app/build/reports/androidTests/connected
destination: reports
# store screenshots for failed ui tests
- store_artifacts:
path: ~/screenshots
destination: screenshots
- build_gradle
- android/start-emulator-and-run-tests:
post-emulator-launch-assemble-command: ./gradlew assemble<< parameters.flavour >>DebugAndroidTest
test-command: ./gradlew connected<< parameters.flavour >>DebugAndroidTest
system-image: system-images;android-25;google_apis;x86
pull-data: true
pull-data-path: /storage/emulated/0/Android/data/
pull-data-target: ~/app-data
# store test reports
- store_artifacts:
path: app/build/reports/androidTests/connected
destination: reports
# store screenshots for failed ui tests
- store_artifacts:
path: ~/app-data
destination: screenshots
deploy_to_play_store:
description: deploy to playstore based on flavour
parameters:
flavour:
type: string
default: "AtlasWeather"
steps:
# The next step will run the unit tests
- android/decode-keystore:
keystore-location: "./app/keystore.jks"
- run:
name: Setup playstore key
command: |
echo "$GOOGLE_PLAY_KEY" > "google-play-key.json"
- build_gradle
- run:
name: Run fastlane command to deploy to playstore
command: |
pwd
bundle exec fastlane deploy<< parameters.flavour >>
- store_test_results:
path: fastlane/report.xml
# Define a job to be invoked later in a workflow.
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
jobs:
@@ -83,54 +109,60 @@ jobs:
- setup_repo
- run_tests:
flavour: << parameters.flavour >>
ui-test-and-release:
# Parameters used for determining
- run_ui_tests:
flavour: << parameters.flavour >>
deploy-to-playstore:
parameters:
flavour:
type: string
default: "AtlasWeather"
executor:
name: android/android-machine
tag: 2023.05.1
default: "Driver"
docker:
- image: cimg/android:2023.07-browsers
auth:
username: ${DOCKER_USERNAME}
password: ${DOCKER_PASSWORD}
steps:
- setup_repo
- run_ui_tests
- run:
name: Setup variables for release
command: |
echo "$RELEASE_KEYSTORE_BASE64" | base64 --decode > "android/app/release_keystore.jks"
echo "$GOOGLE_PLAY_KEY" > "android/playstore.json"
# And finally run the release build
- run:
name: Assemble and Upload to PlayStore
command: |
pwd
bundle exec fastlane deploy<< parameters.flavour >>
- deploy_to_play_store:
flavour: << parameters.flavour >>
# Invoke jobs via workflows
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
workflows:
version: 2
build-release-mono:
jobs:
- build-and-test:
context: appttude
flavour: "MonoWeather"
filters:
branches:
ignore:
- main_atlas
- deploy-to-playstore:
context: appttude
flavour: "MonoWeather"
filters:
branches:
only:
- main_mono
requires:
- build-and-test
build-release-atlas:
jobs:
- build-and-test:
context: appttude
flavour: "AtlasWeather"
- ui-test-and-release:
filters:
branches:
ignore:
- main_mono
- deploy-to-playstore:
context: appttude
flavour: "AtlasWeather"
filters:
branches:
only:
- main_atlas
requires:
- build-and-test
build-release-mono:
jobs:
- build-and-test:
flavour: "MonoWeather"
- ui-test-and-release:
flavour: "MonoWeather"
filters:
branches:
only: main_admin
requires:
- build-and-test

View File

@@ -16,6 +16,19 @@
</AndroidTestResultsTableState>
</value>
</entry>
<entry key="-2008434490">
<value>
<AndroidTestResultsTableState>
<option name="preferredColumnWidths">
<map>
<entry key="Duration" value="90" />
<entry key="Pixel_2_API_27" value="120" />
<entry key="Tests" value="360" />
</map>
</option>
</AndroidTestResultsTableState>
</value>
</entry>
<entry key="170536241">
<value>
<AndroidTestResultsTableState>
@@ -29,6 +42,19 @@
</AndroidTestResultsTableState>
</value>
</entry>
<entry key="287238248">
<value>
<AndroidTestResultsTableState>
<option name="preferredColumnWidths">
<map>
<entry key="Duration" value="90" />
<entry key="Pixel_2_API_27" value="120" />
<entry key="Tests" value="360" />
</map>
</option>
</AndroidTestResultsTableState>
</value>
</entry>
<entry key="1127175145">
<value>
<AndroidTestResultsTableState>

View File

@@ -4,15 +4,19 @@ import android.Manifest
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.internal.inject.InstrumentationContext
import androidx.test.espresso.Espresso
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import com.appttude.h_mal.atlas_weather.application.TestAppClass
import com.appttude.h_mal.atlas_weather.helper.GenericsHelper.getGenericClassAt
import com.appttude.h_mal.atlas_weather.helpers.SnapshotRule
import com.appttude.h_mal.atlas_weather.utils.Stubs
import kotlinx.coroutines.runBlocking
import org.hamcrest.Matcher
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -26,6 +30,7 @@ open class BaseTest<A : Activity>(
lateinit var scenario: ActivityScenario<A>
private lateinit var testApp: TestAppClass
private lateinit var testActivity: Activity
@get:Rule
var permissionRule = GrantPermissionRule.grant(Manifest.permission.ACCESS_COARSE_LOCATION)
@@ -36,19 +41,22 @@ open class BaseTest<A : Activity>(
@Before
fun setUp() {
Screengrab.setDefaultScreenshotStrategy(UiAutomatorScreenshotStrategy())
val startIntent = Intent(InstrumentationRegistry.getInstrumentation().targetContext, activity)
val startIntent =
Intent(InstrumentationRegistry.getInstrumentation().targetContext, activity)
if (intentBundle != null) {
startIntent.replaceExtras(intentBundle)
}
testApp = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
runBlocking {
beforeLaunch()
}
scenario = ActivityScenario.launch(startIntent)
scenario.onActivity {
runBlocking {
testApp = it.application as TestAppClass
beforeLaunch()
}
testActivity = it
afterLaunch()
}
afterLaunch()
}
fun stubEndpoint(url: String, stub: Stubs) {
@@ -59,6 +67,8 @@ open class BaseTest<A : Activity>(
testApp.removeUrlStub(url)
}
fun getActivity() = testActivity
@After
fun tearDown() {
testFinished()
@@ -67,4 +77,14 @@ open class BaseTest<A : Activity>(
open fun beforeLaunch() {}
open fun afterLaunch() {}
open fun testFinished() {}
fun waitFor(delay: Long) {
Espresso.onView(ViewMatchers.isRoot()).perform(object : ViewAction {
override fun getConstraints(): Matcher<View> = ViewMatchers.isRoot()
override fun getDescription(): String = "wait for $delay milliseconds"
override fun perform(uiController: UiController, v: View?) {
uiController.loopMainThreadForAtLeast(delay)
}
})
}
}

View File

@@ -1,6 +1,5 @@
package com.appttude.h_mal.atlas_weather.data.location
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.model.types.LocationType
class MockLocationProvider : LocationProvider {

View File

@@ -7,7 +7,7 @@ import com.appttude.h_mal.atlas_weather.ui.MainActivity
import com.appttude.h_mal.atlas_weather.utils.Stubs
import org.junit.Test
class HomePageUITest : BaseTest<MainActivity>(activity = MainActivity::class.java) {
class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
override fun beforeLaunch() {
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Valid)
@@ -16,6 +16,7 @@ class HomePageUITest : BaseTest<MainActivity>(activity = MainActivity::class.jav
@Test
fun loadApp_validWeatherResponse_returnsValidPage() {
homeScreen {
waitFor(2000)
verifyCurrentTemperature(2)
verifyCurrentLocation("Mock Location")
}

View File

@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.appttude.h_mal.atlas_weather"
tools:node="merge">
xmlns:tools="http://schemas.android.com/tools">
<application
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"

View File

@@ -1,55 +0,0 @@
package com.appttude.h_mal.atlas_weather.ui
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.view.View
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import com.appttude.h_mal.atlas_weather.utils.Event
import com.appttude.h_mal.atlas_weather.utils.displayToast
import com.appttude.h_mal.atlas_weather.utils.hide
import com.appttude.h_mal.atlas_weather.utils.show
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
abstract class BaseFragment: Fragment(){
// toggle visibility of progress spinner while async operations are taking place
fun progressBarStateObserver(progressBar: View) = Observer<Event<Boolean>> {
when(it.getContentIfNotHandled()){
true -> {
progressBar.show()
}
false -> {
progressBar.hide()
}
}
}
// display a toast when operation fails
fun errorObserver() = Observer<Event<String>> {
it.getContentIfNotHandled()?.let { message ->
displayToast(message)
}
}
@SuppressLint("MissingPermission")
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()
}
}
}
}

View File

@@ -0,0 +1,22 @@
package com.appttude.h_mal.atlas_weather.ui.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)
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)
}
}

View File

@@ -0,0 +1,45 @@
package com.appttude.h_mal.atlas_weather.ui.dialog
import android.content.Context
import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
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
lateinit var dialog: AlertDialog
fun showDialog(agreeCallback: () -> Unit = { }, disagreeCallback: () -> Unit = { }) {
val myMessage = buildMessage()
val builder = AlertDialog.Builder(context)
.setPositiveButton("agree") { _, _ ->
agreeCallback()
}
.setNegativeButton("disagree") { _, _ ->
disagreeCallback()
}
.setMessage(myMessage)
.setCancelable(false)
dialog = builder.create()
dialog.show()
// Make the textview clickable. Must be called after show()
val msgTxt = dialog.findViewById<View>(android.R.id.message) as TextView?
msgTxt?.movementMethod = LinkMovementMethod.getInstance()
}
fun dismiss() = dialog.dismiss()
}

View File

@@ -1,24 +1,27 @@
package com.appttude.h_mal.atlas_weather.ui.home
import android.Manifest
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.observe
import androidx.navigation.Navigation.findNavController
import androidx.navigation.ui.onNavDestinationSelected
import androidx.recyclerview.widget.LinearLayoutManager
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.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
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.utils.displayToast
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.MainViewModel
import com.appttude.h_mal.monoWeather.ui.BaseFragment
import kotlinx.android.synthetic.main.fragment_home.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
@@ -30,43 +33,29 @@ import org.kodein.di.generic.instance
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class HomeFragment : BaseFragment(), KodeinAware {
override val kodein by kodein()
private val factory by instance<ApplicationViewModelFactory>()
private val viewModel by activityViewModels<MainViewModel> { factory }
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)
}
class HomeFragment : BaseFragment(R.layout.fragment_home) {
private val viewModel by getFragmentViewModel<MainViewModel>()
@SuppressLint("MissingPermission")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
val recyclerAdapter = WeatherRecyclerAdapter {
val directions =
HomeFragmentDirections.actionHomeFragmentToFurtherDetailsFragment(it)
navigateTo(directions)
}
val recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
navigateToFurtherDetails(it)
})
forecast_listview.apply {
layoutManager = LinearLayoutManager(context)
adapter = recyclerAdapter
}
getPermissionResult(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION_PERMISSION_REQUEST){
viewModel.fetchData()
}
swipe_refresh.apply {
setOnRefreshListener {
getPermissionResult(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION_PERMISSION_REQUEST){
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
viewModel.fetchData()
isRefreshing = true
}
isRefreshing = true
}
}
@@ -76,24 +65,45 @@ class HomeFragment : BaseFragment(), KodeinAware {
viewModel.operationState.observe(viewLifecycleOwner, progressBarStateObserver(progressBar))
viewModel.operationError.observe(viewLifecycleOwner, errorObserver())
viewModel.operationRefresh.observe(viewLifecycleOwner) { it ->
it.getContentIfNotHandled()?.let {
swipe_refresh.isRefreshing = false
}
}
viewModel.operationState.observe(viewLifecycleOwner){
viewModel.operationState.observe(viewLifecycleOwner) {
swipe_refresh.isRefreshing = false
}
}
@SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == LOCATION_PERMISSION_REQUEST) {
if (grantResults.isNotEmpty()
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
viewModel.fetchData()
displayToast("Permission granted")
} else {
displayToast("Permission denied")
}
override fun onStart() {
super.onStart()
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
viewModel.fetchData()
}
}
@SuppressLint("MissingPermission")
override fun permissionsGranted() {
viewModel.fetchData()
}
private fun navigateToFurtherDetails(forecast: Forecast) {
val directions = HomeFragmentDirections
.actionHomeFragmentToFurtherDetailsFragment(forecast)
navigateTo(directions)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
// Inflate the menu; this adds items to the action bar if it is present.
inflater.inflate(R.menu.menu_main, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val navController = findNavController(requireActivity(), R.id.container)
return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item)
}
}

View File

@@ -17,7 +17,7 @@ class ViewHolderCurrent(listItemView: View) : RecyclerView.ViewHolder(listItemVi
var tempUnit: TextView = listItemView.findViewById(R.id.temp_unit_4)
fun bindData(weather: WeatherDisplay?){
locationTV.text = weather?.location
locationTV.text = weather?.displayName
conditionTV.text = weather?.description
weatherIV.loadImage(weather?.iconURL)
avgTempTV.text = weather?.averageTemp?.toInt().toString()

View File

@@ -7,28 +7,20 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.observe
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
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.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import com.appttude.h_mal.monoWeather.ui.BaseFragment
import kotlinx.android.synthetic.main.activity_add_forecast.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
class AddLocationFragment : BaseFragment(), KodeinAware {
override val kodein by kodein()
private val factory by instance<ApplicationViewModelFactory>()
class AddLocationFragment : BaseFragment(R.layout.activity_add_forecast) {
private val viewModel by viewModels<WorldViewModel> { factory }
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)
}
private val viewModel by getFragmentViewModel<WorldViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View File

@@ -9,12 +9,10 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.ui.world.WorldRecyclerAdapter
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
import com.appttude.h_mal.atlas_weather.ui.world.WorldFragmentDirections
import com.appttude.h_mal.atlas_weather.utils.navigateTo
import com.appttude.h_mal.atlas_weather.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
import com.appttude.h_mal.monoWeather.ui.BaseFragment
import kotlinx.android.synthetic.main.fragment_add_location.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
@@ -25,17 +23,8 @@ import org.kodein.di.generic.instance
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class WorldFragment : BaseFragment(), KodeinAware {
override val kodein by kodein()
private val factory by instance<ApplicationViewModelFactory>()
val viewModel by viewModels<WorldViewModel> { factory }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_add_location, container, false)
}
class WorldFragment : BaseFragment(R.layout.fragment_add_location) {
val viewModel by getFragmentViewModel<WorldViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@color/colour_two"
android:centerColor="@color/colour_three"
android:endColor="@color/colour_four"
android:type="linear"
android:angle="45"/>
</shape>

Binary file not shown.

View File

@@ -76,7 +76,7 @@
android:id="@+id/icon_main_4"
android:layout_width="64dp"
android:layout_height="64dp"
tools:src="@drawable/day_305" />
tools:srcCompat="@drawable/water_drop" />
<LinearLayout
android:layout_width="0dp"

View File

@@ -48,7 +48,9 @@
android:id="@+id/list_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
tools:src="@drawable/day_305" />
android:adjustViewBounds="true"
tools:layout_width="32dp"
tools:srcCompat="@drawable/water_drop" />
<LinearLayout
android:layout_width="0dp"

View File

@@ -55,4 +55,8 @@
app:argType="com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay" />
</fragment>
<fragment
android:id="@+id/settings_fragment"
android:name="com.appttude.h_mal.atlas_weather.ui.settings.SettingsFragment"
android:label="SettingsFragment" />
</navigation>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="colour_one">#E8D0DD</color>
<color name="colour_two">#5F8E7B</color>
<color name="colour_three">#B3C0CA</color>
<color name="colour_four">#8C98AD</color>
<color name="colour_five">#2E3532</color>
</resources>

View File

@@ -3,24 +3,27 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@android:color/black</item>
<item name="colorPrimary">@android:color/transparent</item>
<item name="colorPrimaryDark">@color/colour_four</item>
<item name="colorAccent">@color/colour_one</item>
<item name="android:windowBackground">@drawable/gradient</item>
<item name="fontFamily">sans-serif-light</item>
<item name="android:textColor">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/gradient</item>
</style>
<style name="TextAppearance.AppCompat.Widget.ActionBar.Title" parent="@android:style/TextAppearance">
<item name="android:fontFamily">@font/archeologicaps</item>
<!--<item name="android:textColor">@color/colour_five</item>-->
</style>
<style name="titlebar" parent="@android:style/TextAppearance">
<item name="android:fontFamily">@font/archeologicaps</item>
<!--<item name="android:textColor">@color/colour_five</item>-->
</style>
@@ -42,12 +45,4 @@
<item name="android:textSize">32sp</item>
</style>
<style name="icon_style__further_deatils">
<item name="android:layout_width">64dp</item>
<item name="android:layout_height">64dp</item>
<item name="android:adjustViewBounds">true</item>
<item name="android:layout_gravity">center</item>
<item name="android:tint">@color/colorAccent</item>
</style>
</resources>
</resources>

View File

@@ -7,6 +7,8 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<application android:networkSecurityConfig="@xml/network_security_config" />

View File

@@ -26,7 +26,7 @@ import kotlin.coroutines.suspendCoroutine
class LocationProviderImpl(
private val applicationContext: Context
) : com.appttude.h_mal.atlas_weather.data.location.LocationProvider, com.appttude.h_mal.atlas_weather.data.location.LocationHelper(applicationContext) {
) : LocationProvider, LocationHelper(applicationContext) {
private var locationManager =
applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
private val client = FusedLocationProviderClient(applicationContext)

View File

@@ -26,7 +26,7 @@
android:layout_height="match_parent"
android:background="@android:color/black"
android:visibility="gone"
tools:visibility="visible">
tools:visibility="gone">
<ProgressBar
android:layout_gravity="center"
style="?android:attr/progressBarStyle"

View File

@@ -27,4 +27,12 @@
<item name="android:textColor">#ffffff</item>
<item name="android:textSize">12sp</item>
</style>
<style name="icon_style__further_deatils">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:adjustViewBounds">true</item>
<item name="android:layout_gravity">center</item>
<item name="android:tint">@color/colorAccent</item>
</style>
</resources>

View File

@@ -2,9 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<application
android:name="com.appttude.h_mal.atlas_weather.application.AppClass"
android:allowBackup="true"

View File

@@ -66,12 +66,4 @@
<item name="android:textSize">32sp</item>
</style>
<style name="icon_style__further_deatils">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:adjustViewBounds">true</item>
<item name="android:layout_gravity">center</item>
<item name="android:tint">@color/colorAccent</item>
</style>
</resources>

View File

@@ -1,2 +0,0 @@
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
package_name("") # e.g. com.krausefx.app

View File

@@ -16,23 +16,30 @@
default_platform(:android)
platform :android do
desc "Runs all the tests"
lane :test do
gradle(task: "test")
desc "Deploy a new Mono Weather version to the Google Play"
lane :deployMono do
gradle(
task: "clean bundle",
flavor: "MonoWeather",
build_type: "Release",
)
upload_to_play_store(
aab: "app/build/outputs/bundle/monoWeather/app-driver-release.aab",
json_key: "google-play-key.json",
package_name: "h_mal.appttude.com.monoWeather")
end
desc "Submit a new Beta Build to Crashlytics Beta"
lane :beta do
gradle(task: "clean assembleRelease")
crashlytics
# sh "your_script.sh"
# You can also use other beta testing services here
end
desc "Deploy a new version to the Google Play"
lane :deploy do
gradle(task: "clean assembleRelease")
upload_to_play_store
desc "Deploy a new Atlas Weather version to the Google Play"
lane :deployMono do
gradle(
task: "clean bundle",
flavor: "AtlasWeather",
build_type: "Release",
)
upload_to_play_store(
aab: "app/build/outputs/bundle/atlasWeather/app-driver-release.aab",
json_key: "google-play-key.json",
package_name: "h_mal.appttude.com.monoWeather")
end
end