mirror of
https://github.com/ev-map/EVMap.git
synced 2026-01-22 05:47:54 -05:00
Compare commits
9 Commits
replace-be
...
screenshot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e1e22a44b | ||
|
|
d638a88f4d | ||
|
|
b9c08a8e75 | ||
|
|
ab2a77d25a | ||
|
|
e0ddc9f734 | ||
|
|
168fa244d3 | ||
|
|
76388ccc6e | ||
|
|
76aac7dae4 | ||
|
|
ee8b586079 |
115
.github/workflows/screenshots.yml
vendored
Normal file
115
.github/workflows/screenshots.yml
vendored
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
name: Generate Screenshots
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
screenshot:
|
||||||
|
name: Generate screenshots
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
device:
|
||||||
|
- profile: pixel_6
|
||||||
|
api-level: 34
|
||||||
|
display_size: 1080x2336 # subtracted the 64px bottom navigation bar
|
||||||
|
type: phone
|
||||||
|
- profile: pixel_tablet
|
||||||
|
api-level: 34
|
||||||
|
display_size: 2560x1488 # subtracted the 64px navigation bar and 48px status bar
|
||||||
|
type: tenInch
|
||||||
|
env:
|
||||||
|
ANDROID_USER_HOME: /home/runner/.android
|
||||||
|
steps:
|
||||||
|
- name: Enable KVM group perms
|
||||||
|
run: |
|
||||||
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||||
|
sudo udevadm control --reload-rules
|
||||||
|
sudo udevadm trigger --name-match=kvm
|
||||||
|
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Retrieve debug keystore
|
||||||
|
env:
|
||||||
|
DEBUG_KEYSTORE_BASE64: ${{ secrets.DEBUG_KEYSTORE_BASE64 }}
|
||||||
|
run: |
|
||||||
|
mkdir ~/.config/.android
|
||||||
|
echo $DEBUG_KEYSTORE_BASE64 | base64 --decode > ~/.config/.android/debug.keystore
|
||||||
|
|
||||||
|
- name: Set up Java environment
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: 17
|
||||||
|
distribution: 'zulu'
|
||||||
|
cache: 'gradle'
|
||||||
|
|
||||||
|
- name: Setup Android SDK
|
||||||
|
run: |
|
||||||
|
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install "cmdline-tools;latest"
|
||||||
|
rm -r $ANDROID_HOME/cmdline-tools/latest
|
||||||
|
mv $ANDROID_HOME/cmdline-tools/latest-2 $ANDROID_HOME/cmdline-tools/latest
|
||||||
|
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --version
|
||||||
|
|
||||||
|
- name: AVD cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: avd-cache
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.android/avd/*
|
||||||
|
~/.android/adb*
|
||||||
|
key: ${{ runner.os }}-avd-api${{ matrix.device.api-level }}-${{ matrix.device.profile }}
|
||||||
|
|
||||||
|
- name: create AVD and generate snapshot for caching
|
||||||
|
if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.device.api-level }}
|
||||||
|
target: google_atd
|
||||||
|
arch: x86_64
|
||||||
|
profile: ${{ matrix.device.profile }}
|
||||||
|
force-avd-creation: false
|
||||||
|
ram-size: 2048M
|
||||||
|
disk-size: 4096M
|
||||||
|
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
|
||||||
|
disable-animations: true
|
||||||
|
script: echo "Generated AVD snapshot for caching."
|
||||||
|
|
||||||
|
- name: Build app
|
||||||
|
run: ./gradlew assembleGoogleNormalDebug assembleGoogleNormalAndroidTest
|
||||||
|
env:
|
||||||
|
GOINGELECTRIC_API_KEY: ${{ secrets.GOINGELECTRIC_API_KEY }}
|
||||||
|
OPENCHARGEMAP_API_KEY: ${{ secrets.OPENCHARGEMAP_API_KEY }}
|
||||||
|
MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }}
|
||||||
|
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
|
||||||
|
FRONYX_API_KEY: ${{ secrets.FRONYX_API_KEY }}
|
||||||
|
ACRA_CRASHREPORT_CREDENTIALS: ${{ secrets.ACRA_CRASHREPORT_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Run emulator and generate screenshots
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.device.api-level }}
|
||||||
|
target: google_atd
|
||||||
|
arch: x86_64
|
||||||
|
profile: ${{ matrix.device.profile }}
|
||||||
|
force-avd-creation: false
|
||||||
|
ram-size: 2048M
|
||||||
|
disk-size: 4096M
|
||||||
|
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
|
||||||
|
disable-animations: true
|
||||||
|
script: |
|
||||||
|
adb shell pm clear net.vonforst.evmap.debug || true
|
||||||
|
adb shell wm size ${{ matrix.device.display_size }}
|
||||||
|
adb logcat -c
|
||||||
|
|
||||||
|
adb logcat *:E &
|
||||||
|
fastlane screengrab --app_apk_path app/build/outputs/apk/googleNormal/debug/app-google-normal-debug.apk --test_apk_path app/build/outputs/apk/androidTest/googleNormal/debug/app-google-normal-debug-androidTest.apk --tests_package_name=net.vonforst.evmap.debug.test --app_package_name net.vonforst.evmap.debug -p net.vonforst.evmap.screenshot --use_timestamp_suffix false --clear_previous_screenshots true --device_type=${{ matrix.device.type }} -q en-US,de-DE,fr-FR,nb-rNO,nl-NL,pt-PT,ro-RO
|
||||||
|
|
||||||
|
- name: Upload screenshots as artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: screenshots-${{ matrix.device.profile }}-${{ matrix.device.api-level }}
|
||||||
|
path: fastlane/metadata/android
|
||||||
@@ -325,6 +325,7 @@ dependencies {
|
|||||||
debugImplementation("com.facebook.flipper:flipper:0.238.0")
|
debugImplementation("com.facebook.flipper:flipper:0.238.0")
|
||||||
debugImplementation("com.facebook.soloader:soloader:0.10.5")
|
debugImplementation("com.facebook.soloader:soloader:0.10.5")
|
||||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:0.238.0")
|
debugImplementation("com.facebook.flipper:flipper-network-plugin:0.238.0")
|
||||||
|
debugImplementation("androidx.test.espresso:espresso-idling-resource:3.5.1")
|
||||||
|
|
||||||
// testing
|
// testing
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
@@ -335,11 +336,14 @@ dependencies {
|
|||||||
testImplementation("androidx.test:core:1.5.0")
|
testImplementation("androidx.test:core:1.5.0")
|
||||||
testImplementation("androidx.arch.core:core-testing:2.2.0")
|
testImplementation("androidx.arch.core:core-testing:2.2.0")
|
||||||
testImplementation("androidx.car.app:app-testing:$carAppVersion")
|
testImplementation("androidx.car.app:app-testing:$carAppVersion")
|
||||||
testImplementation("androidx.test:core:1.5.0")
|
|
||||||
|
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
|
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
|
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1")
|
||||||
|
androidTestImplementation("androidx.test:rules:1.5.0")
|
||||||
androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
|
androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
|
||||||
|
androidTestImplementation("tools.fastlane:screengrab:2.1.1")
|
||||||
|
|
||||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.15.0")
|
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.15.0")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.vonforst.evmap.screenshot
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.test.core.app.ActivityScenario
|
||||||
|
import androidx.test.espresso.IdlingResource
|
||||||
|
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An espresso idling resource implementation that reports idle status for all data binding
|
||||||
|
* layouts. Data Binding uses a mechanism to post messages which Espresso doesn't track yet.
|
||||||
|
*
|
||||||
|
* Since this application only uses fragments, the resource only checks the fragments and their
|
||||||
|
* children instead of the whole view tree.
|
||||||
|
*
|
||||||
|
* Tracking bug: https://github.com/android/android-test/issues/317
|
||||||
|
*/
|
||||||
|
class DataBindingIdlingResource(
|
||||||
|
activityScenarioRule: ActivityScenarioRule<out FragmentActivity>
|
||||||
|
) : IdlingResource {
|
||||||
|
// list of registered callbacks
|
||||||
|
private val idlingCallbacks = mutableListOf<IdlingResource.ResourceCallback>()
|
||||||
|
|
||||||
|
// give it a unique id to workaround an espresso bug where you cannot register/unregister
|
||||||
|
// an idling resource w/ the same name.
|
||||||
|
private val id = UUID.randomUUID().toString()
|
||||||
|
|
||||||
|
// holds whether isIdle is called and the result was false. We track this to avoid calling
|
||||||
|
// onTransitionToIdle callbacks if Espresso never thought we were idle in the first place.
|
||||||
|
private var wasNotIdle = false
|
||||||
|
|
||||||
|
lateinit var activity: FragmentActivity
|
||||||
|
|
||||||
|
override fun getName() = "DataBinding $id"
|
||||||
|
|
||||||
|
init {
|
||||||
|
monitorActivity(activityScenarioRule.scenario)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIdleNow(): Boolean {
|
||||||
|
val idle = !getBindings().any { it.hasPendingBindings() }
|
||||||
|
@Suppress("LiftReturnOrAssignment")
|
||||||
|
if (idle) {
|
||||||
|
if (wasNotIdle) {
|
||||||
|
// notify observers to avoid espresso race detector
|
||||||
|
idlingCallbacks.forEach { it.onTransitionToIdle() }
|
||||||
|
}
|
||||||
|
wasNotIdle = false
|
||||||
|
} else {
|
||||||
|
wasNotIdle = true
|
||||||
|
// check next frame
|
||||||
|
activity.findViewById<View>(android.R.id.content).postDelayed({
|
||||||
|
isIdleNow
|
||||||
|
}, 16)
|
||||||
|
}
|
||||||
|
return idle
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
|
||||||
|
idlingCallbacks.add(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the activity from an [ActivityScenario] to be used from [DataBindingIdlingResource].
|
||||||
|
*/
|
||||||
|
private fun monitorActivity(
|
||||||
|
activityScenario: ActivityScenario<out FragmentActivity>
|
||||||
|
) {
|
||||||
|
activityScenario.onActivity {
|
||||||
|
this.activity = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all binding classes in all currently available fragments.
|
||||||
|
*/
|
||||||
|
private fun getBindings(): List<ViewDataBinding> {
|
||||||
|
val fragments = (activity as? FragmentActivity)
|
||||||
|
?.supportFragmentManager
|
||||||
|
?.fragments
|
||||||
|
|
||||||
|
val bindings =
|
||||||
|
fragments?.mapNotNull {
|
||||||
|
it.view?.getBinding()
|
||||||
|
} ?: emptyList()
|
||||||
|
val childrenBindings = fragments?.flatMap { it.childFragmentManager.fragments }
|
||||||
|
?.mapNotNull { it.view?.getBinding() } ?: emptyList()
|
||||||
|
|
||||||
|
return bindings + childrenBindings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun View.getBinding(): ViewDataBinding? = DataBindingUtil.getBinding(this)
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package net.vonforst.evmap.screenshot
|
||||||
|
|
||||||
|
import android.Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.IdlingRegistry
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.action.ViewActions.pressBack
|
||||||
|
import androidx.test.espresso.contrib.DrawerActions
|
||||||
|
import androidx.test.espresso.contrib.NavigationViewActions
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.rule.GrantPermissionRule
|
||||||
|
import androidx.test.rule.GrantPermissionRule.grant
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import net.vonforst.evmap.EXTRA_CHARGER_ID
|
||||||
|
import net.vonforst.evmap.EXTRA_LAT
|
||||||
|
import net.vonforst.evmap.EXTRA_LON
|
||||||
|
import net.vonforst.evmap.EspressoIdlingResource
|
||||||
|
import net.vonforst.evmap.MapsActivity
|
||||||
|
import net.vonforst.evmap.R
|
||||||
|
import net.vonforst.evmap.api.goingelectric.GEReferenceData
|
||||||
|
import net.vonforst.evmap.api.goingelectric.GoingElectricApiWrapper
|
||||||
|
import net.vonforst.evmap.model.Favorite
|
||||||
|
import net.vonforst.evmap.storage.AppDatabase
|
||||||
|
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import tools.fastlane.screengrab.Screengrab
|
||||||
|
import tools.fastlane.screengrab.locale.LocaleTestRule
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ScreenshotTest {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
@BeforeClass
|
||||||
|
fun beforeAll() {
|
||||||
|
IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
|
||||||
|
|
||||||
|
Screengrab.setDefaultScreenshotStrategy { screenshotName, screenshotCallback ->
|
||||||
|
screenshotCallback.screenshotCaptured(
|
||||||
|
screenshotName,
|
||||||
|
androidx.test.core.app.takeScreenshot()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
val prefs = PreferenceDataSource(context)
|
||||||
|
prefs.dataSourceSet = true
|
||||||
|
prefs.welcomeDialogShown = true
|
||||||
|
prefs.privacyAccepted = true
|
||||||
|
prefs.opensourceDonationsDialogLastShown = Instant.now()
|
||||||
|
prefs.chargepriceMyVehicles = setOf("b58bc94d-d929-ad71-d95b-08b877bf76ba")
|
||||||
|
prefs.appStartCounter = 0
|
||||||
|
prefs.mapProvider = "google"
|
||||||
|
|
||||||
|
// insert favorites
|
||||||
|
val db = AppDatabase.getInstance(context)
|
||||||
|
val api = GoingElectricApiWrapper(
|
||||||
|
context.getString(R.string.goingelectric_key),
|
||||||
|
context = context
|
||||||
|
)
|
||||||
|
val ids = listOf(70774L to true, 40315L to true, 65330L to true, 62489L to false)
|
||||||
|
runBlocking {
|
||||||
|
val refData = api.getReferenceData().data as GEReferenceData
|
||||||
|
ids.forEachIndexed { i, (id, favorite) ->
|
||||||
|
val detail = api.getChargepointDetail(refData, id).data!!
|
||||||
|
db.chargeLocationsDao().insert(detail)
|
||||||
|
if (db.favoritesDao().findFavorite(id, "goingelectric") == null && favorite) {
|
||||||
|
db.favoritesDao().insert(
|
||||||
|
Favorite(
|
||||||
|
chargerId = id,
|
||||||
|
chargerDataSource = "goingelectric"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val localeTestRule = LocaleTestRule()
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val activityRule: ActivityScenarioRule<MapsActivity> = ActivityScenarioRule(
|
||||||
|
Intent(
|
||||||
|
InstrumentationRegistry.getInstrumentation().targetContext,
|
||||||
|
MapsActivity::class.java
|
||||||
|
).apply {
|
||||||
|
putExtra(EXTRA_CHARGER_ID, 62489L)
|
||||||
|
putExtra(EXTRA_LAT, 53.099512)
|
||||||
|
putExtra(EXTRA_LON, 9.981884)
|
||||||
|
})
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val permissionRule: GrantPermissionRule = grant(ACCESS_FINE_LOCATION)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
IdlingRegistry.getInstance().register(DataBindingIdlingResource(activityRule))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTakeScreenshot() {
|
||||||
|
Thread.sleep(15000L)
|
||||||
|
Screengrab.screenshot("01_map_google")
|
||||||
|
|
||||||
|
onView(withId(R.id.topPart)).perform(click())
|
||||||
|
|
||||||
|
Thread.sleep(1000L)
|
||||||
|
Screengrab.screenshot("02_detail")
|
||||||
|
|
||||||
|
onView(withId(R.id.btnChargeprice)).perform(click())
|
||||||
|
Thread.sleep(5000L)
|
||||||
|
Screengrab.screenshot("03_prices")
|
||||||
|
|
||||||
|
onView(isRoot()).perform(pressBack())
|
||||||
|
Thread.sleep(500L)
|
||||||
|
onView(isRoot()).perform(pressBack())
|
||||||
|
|
||||||
|
Thread.sleep(2000L)
|
||||||
|
|
||||||
|
onView(withId(R.id.menu_filter)).perform(click())
|
||||||
|
Thread.sleep(1000L)
|
||||||
|
onView(withText(R.string.menu_edit_filters)).perform(click())
|
||||||
|
|
||||||
|
Thread.sleep(1000L)
|
||||||
|
|
||||||
|
Screengrab.screenshot("05_filters")
|
||||||
|
onView(isRoot()).perform(pressBack())
|
||||||
|
|
||||||
|
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
|
||||||
|
onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.favs))
|
||||||
|
|
||||||
|
Thread.sleep(10000L)
|
||||||
|
Screengrab.screenshot("04_favorites")
|
||||||
|
|
||||||
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
PreferenceDataSource(context).mapProvider = "mapbox"
|
||||||
|
onView(isRoot()).perform(pressBack())
|
||||||
|
|
||||||
|
Thread.sleep(5000L)
|
||||||
|
Screengrab.screenshot("01_map_mapbox")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.johan.evmap.storage
|
package net.vonforst.evmap.storage
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
@@ -1,5 +1,25 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Permissions needed for fastlane screengrab -->
|
||||||
|
|
||||||
|
<!-- Allows unlocking your device and activating its screen so UI tests can succeed -->
|
||||||
|
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
|
<!-- Allows for storing and retrieving screenshots -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<!-- Allows changing locales -->
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.CHANGE_CONFIGURATION"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
|
<!-- Allows changing SystemUI demo mode -->
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.DUMP"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package net.vonforst.evmap
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.test.espresso.IdlingRegistry
|
||||||
|
import androidx.test.espresso.idling.CountingIdlingResource
|
||||||
import com.facebook.flipper.android.AndroidFlipperClient
|
import com.facebook.flipper.android.AndroidFlipperClient
|
||||||
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin
|
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin
|
||||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping
|
import com.facebook.flipper.plugins.inspector.DescriptorMapping
|
||||||
@@ -34,9 +36,29 @@ fun OkHttpClient.Builder.addDebugInterceptors(): OkHttpClient.Builder {
|
|||||||
} catch (e: ClassNotFoundException) {
|
} catch (e: ClassNotFoundException) {
|
||||||
isRunningTest = false
|
isRunningTest = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isRunningTest) {
|
if (!isRunningTest) {
|
||||||
this.addNetworkInterceptor(FlipperOkhttpInterceptor(networkFlipperPlugin))
|
this.addNetworkInterceptor(FlipperOkhttpInterceptor(networkFlipperPlugin))
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a static reference to [IdlingResource], only available in the 'debug' build type.
|
||||||
|
*/
|
||||||
|
object EspressoIdlingResource {
|
||||||
|
private const val RESOURCE = "GLOBAL"
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val countingIdlingResource = CountingIdlingResource(RESOURCE)
|
||||||
|
|
||||||
|
fun increment() {
|
||||||
|
countingIdlingResource.increment()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decrement() {
|
||||||
|
if (!countingIdlingResource.isIdleNow) {
|
||||||
|
countingIdlingResource.decrement()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.vonforst.evmap
|
package net.vonforst.evmap
|
||||||
|
|
||||||
|
import android.app.ActivityOptions
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -206,18 +207,15 @@ class MapsActivity : AppCompatActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (intent.hasExtra(EXTRA_CHARGER_ID)) {
|
} else if (intent.hasExtra(EXTRA_CHARGER_ID)) {
|
||||||
deepLink = navController.createDeepLink()
|
navController.navigate(
|
||||||
.setDestination(R.id.map)
|
R.id.map, MapFragmentArgs(
|
||||||
.setArguments(
|
chargerId = intent.getLongExtra(EXTRA_CHARGER_ID, 0),
|
||||||
MapFragmentArgs(
|
latLng = LatLng(
|
||||||
chargerId = intent.getLongExtra(EXTRA_CHARGER_ID, 0),
|
intent.getDoubleExtra(EXTRA_LAT, 0.0),
|
||||||
latLng = LatLng(
|
intent.getDoubleExtra(EXTRA_LON, 0.0)
|
||||||
intent.getDoubleExtra(EXTRA_LAT, 0.0),
|
)
|
||||||
intent.getDoubleExtra(EXTRA_LON, 0.0)
|
).toBundle()
|
||||||
)
|
)
|
||||||
).toBundle()
|
|
||||||
)
|
|
||||||
.createPendingIntent()
|
|
||||||
} else if (intent.hasExtra(EXTRA_FAVORITES)) {
|
} else if (intent.hasExtra(EXTRA_FAVORITES)) {
|
||||||
deepLink = navController.createDeepLink()
|
deepLink = navController.createDeepLink()
|
||||||
.setGraph(navGraph)
|
.setGraph(navGraph)
|
||||||
|
|||||||
@@ -108,11 +108,14 @@ class PreferenceDataSource(val context: Context) {
|
|||||||
val darkmode: String
|
val darkmode: String
|
||||||
get() = sp.getString("darkmode", "default")!!
|
get() = sp.getString("darkmode", "default")!!
|
||||||
|
|
||||||
val mapProvider: String
|
var mapProvider: String
|
||||||
get() = sp.getString(
|
get() = sp.getString(
|
||||||
"map_provider",
|
"map_provider",
|
||||||
context.getString(R.string.pref_map_provider_default)
|
context.getString(R.string.pref_map_provider_default)
|
||||||
)!!
|
)!!
|
||||||
|
set(value) {
|
||||||
|
sp.edit().putString("map_provider", value).apply()
|
||||||
|
}
|
||||||
|
|
||||||
var searchProvider: String
|
var searchProvider: String
|
||||||
get() = sp.getString(
|
get() = sp.getString(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import jsonapi.Relationships
|
|||||||
import jsonapi.ResourceIdentifier
|
import jsonapi.ResourceIdentifier
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import net.vonforst.evmap.EspressoIdlingResource
|
||||||
import net.vonforst.evmap.api.chargeprice.*
|
import net.vonforst.evmap.api.chargeprice.*
|
||||||
import net.vonforst.evmap.api.equivalentPlugTypes
|
import net.vonforst.evmap.api.equivalentPlugTypes
|
||||||
import net.vonforst.evmap.model.ChargeLocation
|
import net.vonforst.evmap.model.ChargeLocation
|
||||||
@@ -48,6 +49,7 @@ class ChargepriceViewModel(
|
|||||||
} else {
|
} else {
|
||||||
value = Resource.loading(null)
|
value = Resource.loading(null)
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
EspressoIdlingResource.increment()
|
||||||
value = try {
|
value = try {
|
||||||
val result = api.getVehicles()
|
val result = api.getVehicles()
|
||||||
Resource.success(result.filter {
|
Resource.success(result.filter {
|
||||||
@@ -57,6 +59,8 @@ class ChargepriceViewModel(
|
|||||||
Resource.error(e.message, null)
|
Resource.error(e.message, null)
|
||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
Resource.error(e.message, null)
|
Resource.error(e.message, null)
|
||||||
|
} finally {
|
||||||
|
EspressoIdlingResource.decrement()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,6 +257,7 @@ class ChargepriceViewModel(
|
|||||||
|
|
||||||
loadPricesJob?.cancel()
|
loadPricesJob?.cancel()
|
||||||
loadPricesJob = viewModelScope.launch {
|
loadPricesJob = viewModelScope.launch {
|
||||||
|
EspressoIdlingResource.increment()
|
||||||
try {
|
try {
|
||||||
val result = api.getChargePrices(
|
val result = api.getChargePrices(
|
||||||
ChargepriceRequest(
|
ChargepriceRequest(
|
||||||
@@ -295,6 +300,8 @@ class ChargepriceViewModel(
|
|||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
chargePrices.value = Resource.error(e.message, null)
|
chargePrices.value = Resource.error(e.message, null)
|
||||||
chargePriceMeta.value = Resource.error(e.message, null)
|
chargePriceMeta.value = Resource.error(e.message, null)
|
||||||
|
} finally {
|
||||||
|
EspressoIdlingResource.decrement()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,10 @@ fun addDebugInterceptors(context: Context) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OkHttpClient.Builder.addDebugInterceptors(): OkHttpClient.Builder = this
|
fun OkHttpClient.Builder.addDebugInterceptors(): OkHttpClient.Builder = this
|
||||||
|
|
||||||
|
object EspressoIdlingResource {
|
||||||
|
fun increment() {}
|
||||||
|
|
||||||
|
fun decrement() {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user