- Test suite expanded

- config.yml updated
 -
This commit is contained in:
2023-08-29 14:02:42 +01:00
parent 07d7e6cbe7
commit ce5be16232
38 changed files with 631 additions and 447 deletions

View File

@@ -1,26 +1,130 @@
# Use the latest 2.1 version of CircleCI pipeline process engine. # Use the latest 2.1 version of CircleCI pipeline process engine.
# See: https://circleci.com/docs/configuration-reference # See: https://circleci.com/docs/2.0/configuration-reference
# For a detailed guide to building and testing on Android, read the docs:
# https://circleci.com/docs/2.0/language-android/ for more details.
version: 2.1 version: 2.1
# Define a job to be invoked later in a workflow. # Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
# See: https://circleci.com/docs/configuration-reference/#jobs # See: https://circleci.com/docs/2.0/orb-intro/
jobs: orbs:
say-hello: android: circleci/android@2.3.0
# Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub.
# See: https://circleci.com/docs/configuration-reference/#executor-job commands:
docker: setup_repo:
- image: cimg/base:stable description: checkout repo and android dependencies
# Add steps to the job
# See: https://circleci.com/docs/configuration-reference/#steps
steps: steps:
- checkout - checkout
- run: - run:
name: "Say hello" name: Give gradle permissions
command: "echo Hello, World!" command: |
sudo chmod +x ./gradlew
# Orchestrate jobs using workflows - android/restore-gradle-cache
# See: https://circleci.com/docs/configuration-reference/#workflows run_tests:
description: run tests for flavour specified
steps:
# The next step will run the unit tests
- run:
name: Run local unit tests
command: |
./gradlew testDebugUnitTest
- android/save-gradle-cache
- store_artifacts:
path: app/build/reports
destination: reports
- store_test_results:
path: app/build/test-results
run_ui_tests:
description: run instrumentation and espresso tests
steps:
- android/start-emulator-and-run-tests:
post-emulator-launch-assemble-command: ./gradlew assembleAndroidTest
test-command: ./gradlew connectedDebugAndroidTest --continue
system-image: system-images;android-26;google_apis;x86
# store screenshots for failed ui tests
- when:
condition: on_fail
steps:
- store_artifacts:
path: app/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected
destination: connected_android_test
# store test reports
- store_artifacts:
path: app/build/reports/androidTests/connected
destination: reports
- store_test_results:
path: app/build/outputs/androidTest-results/connected
deploy_to_play_store:
description: deploy to playstore
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"
- run:
name: Run fastlane command to deploy to playstore
command: |
pwd
bundle exec fastlane deploy
- 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:
# Below is the definition of your job to build and test your app, you can rename and customize it as you want.
build-and-test:
# 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_tests
run_instrumentation_test:
# 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
deploy-to-playstore:
docker:
- image: cimg/android:2023.07-browsers
auth:
username: ${DOCKER_USERNAME}
password: ${DOCKER_PASSWORD}
steps:
- setup_repo
- deploy_to_play_store
# Invoke jobs via workflows
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
workflows: workflows:
say-hello-workflow: version: 2
build-release:
jobs: jobs:
- say-hello - build-and-test:
context: appttude
- run_instrumentation_test:
context: appttude
filters:
branches:
only:
- master
- release
- deploy-to-playstore:
context: appttude
filters:
branches:
only:
- release
requires:
- build-and-test

2
.gitignore vendored
View File

@@ -88,7 +88,7 @@ gen-external-apklibs
.idea/uiDesigner.xml .idea/uiDesigner.xml
.idea/assetWizardSettings.xml .idea/assetWizardSettings.xml
.idea/gradle.xml .idea/gradle.xml
.idea/jarRepositorie .idea/jarRepositories.xml
# Gem/fastlane # Gem/fastlane
Gemfile.lock Gemfile.lock

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidTestResultsUserPreferences">
<option name="androidTestResultsTableState">
<map>
<entry key="1283002349">
<value>
<AndroidTestResultsTableState>
<option name="preferredColumnWidths">
<map>
<entry key="Duration" value="90" />
<entry key="Pixel_6_Pro_API_31" value="120" />
<entry key="Tests" value="360" />
</map>
</option>
</AndroidTestResultsTableState>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

@@ -1,127 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="imageAssetPanel">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="actionbar">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="opacityPercent" value="80" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="theme" value="HOLO_DARK" />
<entry key="themeColor" value="ffffff" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="launcher">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="scalingPercent" value="69" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="foregroundImage" value="C:\Users\h_mal\Desktop\Farmr\farmicon.png" />
<entry key="outputName" value="ic_launcher_release" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="launcherLegacy">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="notification">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>

Binary file not shown.

Binary file not shown.

6
.idea/compiler.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

21
.idea/gradle.xml generated
View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="delegatedBuild" value="false" />
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

47
.idea/misc.xml generated
View File

@@ -1,47 +0,0 @@
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="12">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="android.annotation.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="11">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="android.annotation.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

12
.idea/modules.xml generated
View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Farmr.iml" filepath="$PROJECT_DIR$/.idea/modules/Farmr.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Farmr.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Farmr.app.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Farmr.app.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Farmr.app.androidTest.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Farmr.app.main.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Farmr.app.main.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Farmr.app.unitTest.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Farmr.app.unitTest.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -10,7 +10,7 @@ android {
targetSdkVersion 31 targetSdkVersion 31
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' testInstrumentationRunner 'com.appttude.h_mal.farmr.application.TestRunner'
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
buildTypes { buildTypes {
@@ -19,6 +19,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
useLibrary 'android.test.mock'
} }
dependencies { dependencies {

View File

@@ -0,0 +1,38 @@
package com.appttude.h_mal.farmr.application
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.idling.CountingIdlingResource
import androidx.test.platform.app.InstrumentationRegistry
import com.appttude.h_mal.farmr.base.BaseApplication
import com.appttude.h_mal.farmr.data.legacydb.LegacyDatabase
import com.appttude.h_mal.farmr.data.prefs.PreferenceProvider
import com.appttude.h_mal.farmr.model.Shift
class TestAppClass : BaseApplication() {
private val idlingResources = CountingIdlingResource("Data_loader")
lateinit var database: LegacyDatabase
lateinit var preferenceProvider: PreferenceProvider
override fun onCreate() {
super.onCreate()
IdlingRegistry.getInstance().register(idlingResources)
}
override fun createDatabase(): LegacyDatabase {
database =
LegacyDatabase(InstrumentationRegistry.getInstrumentation().context.contentResolver)
return database
}
override fun createPrefs(): PreferenceProvider {
preferenceProvider = PreferenceProvider(this)
return preferenceProvider
}
fun addToDatabase(shift: Shift) = database.insertShiftDataIntoDatabase(shift)
fun addShiftsToDatabase(shifts: List<Shift>) = shifts.forEach { addToDatabase(it) }
fun clearDatabase() = database.deleteAllShiftsInDatabase()
fun cleanPrefs() = preferenceProvider.clearPrefs()
}

View File

@@ -0,0 +1,21 @@
package com.appttude.h_mal.farmr.application
import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
class TestRunner : AndroidJUnitRunner() {
@Throws(
InstantiationException::class,
IllegalAccessException::class,
ClassNotFoundException::class
)
override fun newApplication(
cl: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(cl, TestAppClass::class.java.name, context)
}
}

View File

@@ -19,6 +19,7 @@ import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry._ID
import com.appttude.h_mal.farmr.data.legacydb.ShiftProvider import com.appttude.h_mal.farmr.data.legacydb.ShiftProvider
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull import junit.framework.TestCase.assertNull
import org.junit.After
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@@ -31,6 +32,11 @@ class ShiftProviderTest {
private val contentResolver: ContentResolver private val contentResolver: ContentResolver
get() = providerRule.resolver get() = providerRule.resolver
@After
fun tearDown() {
contentResolver.delete(CONTENT_URI, null, null)
}
@Test @Test
fun insertEntry_queryEntry_assertEntry() { fun insertEntry_queryEntry_assertEntry() {
// Arrange // Arrange

View File

@@ -1,26 +0,0 @@
package com.appttude.h_mal.farmr.data.ui.robots
import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BaseRecyclerAdapter.CurrentViewHolder
import com.appttude.h_mal.farmr.data.ui.BaseTestRobot
fun homeScreen(func: HomeScreenRobot.() -> Unit) = HomeScreenRobot().apply { func() }
class HomeScreenRobot : BaseTestRobot() {
fun clickOnItem(position: Int) = clickViewInRecyclerAtPosition<CurrentViewHolder>(R.id.list_item_view, position)
fun clickOnItemWithText(text: String) = clickOnRecyclerItemWithText<CurrentViewHolder>(R.id.list_item_view, text)
fun clickOnEdit(position: Int) = clickViewInRecycler<CurrentViewHolder>(R.id.list_item_view, R.id.imageView)
fun clickFab() = clickButton(R.id.fab1)
fun clickOnInfo() = clickButton(R.id.action_favorite)
// fun clearFilter() =
// fun applySort() =
// fun verifyCurrentLocation(location: String) = matchText(R.id.location_main_4, location)
// fun refresh() = pullToRefresh(R.id.swipe_refresh)
// fun isDisplayed() = matchViewWaitFor(R.id.temp_main_4)
// fun verifyUnableToRetrieve() {
// matchText(R.id.header_text, R.string.retrieve_warning)
// matchText(R.id.body_text, R.string.empty_retrieve_warning)
// }
}

View File

@@ -1,77 +0,0 @@
package com.appttude.h_mal.farmr.data.ui.tests
import com.appttude.h_mal.farmr.data.ui.BaseTest
import com.appttude.h_mal.farmr.data.ui.robots.addScreen
import com.appttude.h_mal.farmr.data.ui.robots.homeScreen
import com.appttude.h_mal.farmr.data.ui.robots.viewScreen
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.ui.MainActivity
import org.junit.Test
class ShiftTests: BaseTest<MainActivity>(MainActivity::class.java) {
// Add a shift successfully
@Test
fun test1() {
homeScreen {
clickFab()
}
addScreen {
setDescription("This is a description")
setDate(2023, 2, 11)
clickShiftType(ShiftType.HOURLY)
setTimeIn(12,0)
setTimeOut(14, 30)
setBreakTime(30)
setRateOfPay(10f)
assertDuration("2.0 hours")
assertTotalPay("£20.00")
submit()
}
homeScreen {
sc("This is a description")
}
}
// Edit a shift successfully
@Test
fun test2() {
homeScreen {
clickOnItemWithText("Edit this shift")
}
addScreen {
setRateOfPay(20f)
assertDuration("2.0 hours")
assertTotalPay("£40.00")
submit()
}
homeScreen {
clickOnItemWithText("Edit this shift")
}
viewScreen {
matchDescription("Edit this shift")
matchDuration("2 Hours 0 minutes")
matchTotalPay("2.0 hours @ £20.00 per Hour\nEquals:£40.00")
}
}
// filter the list with date from
@Test
fun test3() {}
// filter the list with date to
@Test
fun test4() {}
// Add a shift as piece rate
@Test
fun test5() {}
// Validate the details screen
@Test
fun test6() {}
// filter, sort, order and then reset
@Test
fun test7() {}
}

View File

@@ -1 +0,0 @@
package com.appttude.h_mal.farmr.data.ui.utils

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.farmr.data.ui package com.appttude.h_mal.farmr.ui
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
@@ -6,7 +6,9 @@ import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso
import androidx.test.espresso.UiController import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction import androidx.test.espresso.ViewAction
@@ -15,13 +17,16 @@ import androidx.test.espresso.matcher.RootMatchers.withDecorView
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule import androidx.test.rule.GrantPermissionRule
import com.appttude.h_mal.farmr.application.TestAppClass
import com.appttude.h_mal.farmr.di.ShiftApplication import com.appttude.h_mal.farmr.di.ShiftApplication
import com.appttude.h_mal.farmr.ui.utils.getShifts
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.hamcrest.Matchers import org.hamcrest.Matchers
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.kodein.di.android.kodein
@Suppress("EmptyMethod") @Suppress("EmptyMethod")
open class BaseTest<A : Activity>( open class BaseTest<A : Activity>(
@@ -30,7 +35,7 @@ open class BaseTest<A : Activity>(
) { ) {
lateinit var scenario: ActivityScenario<A> lateinit var scenario: ActivityScenario<A>
private lateinit var testApp: ShiftApplication private lateinit var testApp: TestAppClass
private lateinit var testActivity: Activity private lateinit var testActivity: Activity
private lateinit var decorView: View private lateinit var decorView: View
@@ -38,7 +43,7 @@ open class BaseTest<A : Activity>(
var permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE) var permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE)
@Before @Before
fun setUp() { open fun setUp() {
val startIntent = val startIntent =
Intent(InstrumentationRegistry.getInstrumentation().targetContext, activity) Intent(InstrumentationRegistry.getInstrumentation().targetContext, activity)
if (intentBundle != null) { if (intentBundle != null) {
@@ -46,13 +51,17 @@ open class BaseTest<A : Activity>(
} }
testApp = testApp =
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ShiftApplication InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
kodein(testApp)
runBlocking { runBlocking {
beforeLaunch() beforeLaunch()
} }
scenario = ActivityScenario.launch(startIntent) scenario = ActivityScenario.launch(startIntent)
scenario.onActivity { scenario.onActivity {
testApp =
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
onLaunch()
decorView = it.window.decorView decorView = it.window.decorView
testActivity = it testActivity = it
} }
@@ -67,6 +76,7 @@ open class BaseTest<A : Activity>(
} }
open fun beforeLaunch() {} open fun beforeLaunch() {}
open fun onLaunch() {}
open fun afterLaunch() {} open fun afterLaunch() {}
open fun testFinished() {} open fun testFinished() {}
@@ -88,4 +98,13 @@ open class BaseTest<A : Activity>(
waitFor(3500) waitFor(3500)
} }
} }
fun navigateBack() = Espresso.pressBack()
fun addRandomShifts() {
testApp.addShiftsToDatabase(getShifts())
}
fun clearDataBase() = testApp.clearDatabase()
fun clearPrefs() = testApp.cleanPrefs()
} }

View File

@@ -1,12 +1,18 @@
package com.appttude.h_mal.farmr.data.ui package com.appttude.h_mal.farmr.ui
import android.content.res.Resources import android.content.res.Resources
import android.view.View
import android.widget.DatePicker import android.widget.DatePicker
import android.widget.TimePicker import android.widget.TimePicker
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.widget.AppCompatButton
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.recyclerview.widget.RecyclerView.ViewHolder
import androidx.test.espresso.Espresso.onData import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.ViewInteraction import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
@@ -14,11 +20,15 @@ import androidx.test.espresso.action.ViewActions.swipeDown
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.PickerActions import androidx.test.espresso.contrib.PickerActions
import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.espresso.matcher.ViewMatchers.*
import com.appttude.h_mal.farmr.data.ui.utils.EspressoHelper.waitForView import androidx.test.platform.app.InstrumentationRegistry
import com.appttude.h_mal.farmr.ui.utils.EspressoHelper.waitForView
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.anything import org.hamcrest.CoreMatchers.anything
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.Matcher
import org.hamcrest.Matchers
@SuppressWarnings("unused") @SuppressWarnings("unused")
open class BaseTestRobot { open class BaseTestRobot {
@@ -54,6 +64,17 @@ open class BaseTestRobot {
.atPosition(position).perform(click()) .atPosition(position).perform(click())
} }
fun clickOnMenuItem(menuId: Int) {
openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().context)
onView(withText(menuId)).perform(click())
}
fun clickDialogButton(text: String) {
onView(withText(text)).inRoot(isDialog())
.check(matches(isDisplayed()))
.perform(click());
}
fun <VH : ViewHolder> scrollToRecyclerItem(recyclerId: Int, text: String): ViewInteraction? { fun <VH : ViewHolder> scrollToRecyclerItem(recyclerId: Int, text: String): ViewInteraction? {
return matchView(recyclerId) return matchView(recyclerId)
.perform( .perform(
@@ -88,14 +109,6 @@ open class BaseTestRobot {
) )
} }
fun <VH : ViewHolder> clickViewInRecycler(recyclerId: Int, text: String) {
matchView(recyclerId)
.perform(
// scrollTo will fail the test if no item matches.
RecyclerViewActions.actionOnItem<VH>(hasDescendant(withText(text)), click())
)
}
fun <VH : ViewHolder> clickViewInRecycler(recyclerId: Int, resIdForString: Int) { fun <VH : ViewHolder> clickViewInRecycler(recyclerId: Int, resIdForString: Int) {
matchView(recyclerId) matchView(recyclerId)
.perform( .perform(
@@ -107,23 +120,52 @@ open class BaseTestRobot {
) )
} }
fun <VH : ViewHolder> clickViewInRecyclerAtPosition(recyclerId: Int, position: Int) { fun <VH : ViewHolder> clickRecyclerAtPosition(recyclerId: Int, position: Int) {
matchView(recyclerId) matchView(recyclerId)
.perform( .perform(
// scrollTo will fail the test if no item matches. // scrollTo will fail the test if no item matches.
RecyclerViewActions.scrollToPosition<VH>(position), RecyclerViewActions.scrollToPosition<VH>(position),
RecyclerViewActions.actionOnItemAtPosition<VH>(position, click()) RecyclerViewActions.actionOnItemAtPosition<VH>(position, click()),
)
}
fun <VH : ViewHolder> clickViewInRecyclerAtPosition(recyclerId: Int, position: Int, subViewId: Int) {
matchView(recyclerId)
.perform(
// scrollTo will fail the test if no item matches.
RecyclerViewActions.scrollToPosition<VH>(position),
RecyclerViewActions.actionOnItemAtPosition<VH>(position, object : ViewAction {
override fun getDescription(): String {
return "click on subview in RecyclerView at position: $position"
}
override fun getConstraints(): Matcher<View> {
return Matchers.allOf(
isAssignableFrom(
RecyclerView::class.java
), isDisplayed()
)
}
override fun perform(uiController: UiController?, view: View?) {
view?.findViewById<View>(subViewId)?.performClick()
}
}),
) )
} }
fun <VH : ViewHolder> clickOnRecyclerItemWithText(recyclerId: Int, text: String) { fun <VH : ViewHolder> clickOnRecyclerItemWithText(recyclerId: Int, text: String) {
scrollToRecyclerItem<VH>(recyclerId, text) matchView(recyclerId).perform(
?.perform( // scrollTo will fail the test if no item matches.
// scrollTo will fail the test if no item matches. RecyclerViewActions.scrollTo<VH>(
RecyclerViewActions.actionOnItem<VH>( hasDescendant(withText(text))
withChild(withText(text)), click() ),
) RecyclerViewActions.actionOnItem<VH>(
hasDescendant(withText(text)),
click()
) )
)
} }
fun swipeDown(resId: Int): ViewInteraction = fun swipeDown(resId: Int): ViewInteraction =
@@ -144,9 +186,17 @@ open class BaseTestRobot {
day day
) )
) )
onView(
allOf(
withClassName(equalTo(AppCompatButton::class.java.name)),
withText("OK")
)
).perform(
click()
)
} }
fun selectTextInSpinner(id: Int, text:String) { fun selectTextInSpinner(id: Int, text: String) {
clickButton(id) clickButton(id)
onView(withSpinnerText(text)).perform(click()) onView(withSpinnerText(text)).perform(click())
} }
@@ -157,5 +207,13 @@ open class BaseTestRobot {
hours, minutes hours, minutes
) )
) )
onView(
allOf(
withClassName(equalTo(AppCompatButton::class.java.name)),
withText("OK")
)
).perform(
click()
)
} }
} }

View File

@@ -1,8 +1,8 @@
package com.appttude.h_mal.farmr.data.ui.robots package com.appttude.h_mal.farmr.ui.robots
import com.appttude.h_mal.farmr.R import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.data.ui.BaseTestRobot
import com.appttude.h_mal.farmr.model.ShiftType import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.ui.BaseTestRobot
fun addScreen(func: AddItemScreenRobot.() -> Unit) = AddItemScreenRobot().apply { func() } fun addScreen(func: AddItemScreenRobot.() -> Unit) = AddItemScreenRobot().apply { func() }
class AddItemScreenRobot : BaseTestRobot() { class AddItemScreenRobot : BaseTestRobot() {

View File

@@ -1,7 +1,7 @@
package com.appttude.h_mal.farmr.data.ui.robots package com.appttude.h_mal.farmr.ui.robots
import com.appttude.h_mal.farmr.R import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.data.ui.BaseTestRobot import com.appttude.h_mal.farmr.ui.BaseTestRobot
import com.appttude.h_mal.farmr.model.ShiftType import com.appttude.h_mal.farmr.model.ShiftType
fun filterScreen(func: FilterScreenRobot.() -> Unit) = FilterScreenRobot().apply { func() } fun filterScreen(func: FilterScreenRobot.() -> Unit) = FilterScreenRobot().apply { func() }

View File

@@ -0,0 +1,29 @@
package com.appttude.h_mal.farmr.ui.robots
import androidx.test.espresso.matcher.RootMatchers.isDialog
import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BaseRecyclerAdapter.CurrentViewHolder
import com.appttude.h_mal.farmr.model.Order
import com.appttude.h_mal.farmr.model.Sortable
import com.appttude.h_mal.farmr.ui.BaseTestRobot
fun homeScreen(func: HomeScreenRobot.() -> Unit) = HomeScreenRobot().apply { func() }
class HomeScreenRobot : BaseTestRobot() {
fun clickOnItemWithText(text: String) = clickOnRecyclerItemWithText<CurrentViewHolder>(R.id.list_item_view, text)
fun clickOnItemAtPosition(position: Int) = clickRecyclerAtPosition<CurrentViewHolder>(R.id.list_item_view, position)
fun clickOnEdit(position: Int) = clickViewInRecyclerAtPosition<CurrentViewHolder>(R.id.list_item_view, position, R.id.imageView)
fun clickFab() = clickButton(R.id.fab1)
fun clickOnInfoIcon() = clickButton(R.id.action_favorite)
fun clickFilterInMenu() = clickOnMenuItem(R.string.filter)
fun clickClearFilterInMenu() = clickOnMenuItem(R.string.clear)
fun clickSortInMenu() = clickOnMenuItem(R.string.sort)
fun applySort(sortable: Sortable, order: Order = Order.ASCENDING) {
clickSortInMenu()
val label = sortable.label
clickDialogButton(label)
val orderLabel = order.label
clickDialogButton(orderLabel)
}
}

View File

@@ -1,7 +1,7 @@
package com.appttude.h_mal.farmr.data.ui.robots package com.appttude.h_mal.farmr.ui.robots
import com.appttude.h_mal.farmr.R import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.data.ui.BaseTestRobot import com.appttude.h_mal.farmr.ui.BaseTestRobot
import com.appttude.h_mal.farmr.model.ShiftType import com.appttude.h_mal.farmr.model.ShiftType
fun viewScreen(func: ViewItemScreenRobot.() -> Unit) = ViewItemScreenRobot().apply { func() } fun viewScreen(func: ViewItemScreenRobot.() -> Unit) = ViewItemScreenRobot().apply { func() }

View File

@@ -0,0 +1,142 @@
package com.appttude.h_mal.farmr.ui.tests
import com.appttude.h_mal.farmr.model.Order
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.model.Sortable
import com.appttude.h_mal.farmr.ui.BaseTest
import com.appttude.h_mal.farmr.ui.MainActivity
import com.appttude.h_mal.farmr.ui.robots.addScreen
import com.appttude.h_mal.farmr.ui.robots.filterScreen
import com.appttude.h_mal.farmr.ui.robots.homeScreen
import com.appttude.h_mal.farmr.ui.robots.viewScreen
import com.appttude.h_mal.farmr.utils.ID
import org.junit.Test
class ShiftTests : BaseTest<MainActivity>(MainActivity::class.java) {
override fun afterLaunch() {
super.afterLaunch()
addRandomShifts()
// Content resolver hard to mock
// Dirty technique to have a populated list
homeScreen {
clickFab()
navigateBack()
}
}
override fun testFinished() {
super.testFinished()
clearDataBase()
clearPrefs()
}
// Add a shift successfully
@Test
fun openAddScreen_addNewShift_newShiftCreated() {
homeScreen {
clickFab()
}
addScreen {
setDescription("This is a description")
setDate(2023, 2, 11)
clickShiftType(ShiftType.HOURLY)
setTimeIn(12, 0)
setTimeOut(14, 30)
setBreakTime(30)
setRateOfPay(10f)
assertDuration("2.0 hours")
assertTotalPay("£20.00")
submit()
}
homeScreen {
clickOnItemWithText("This is a description")
}
}
// Edit a shift successfully
@Test
fun test2() {
homeScreen {
clickOnEdit(0)
}
addScreen {
setDescription("Edited this shift")
setTimeIn(12, 0)
setTimeOut(14, 30)
setBreakTime(30)
setRateOfPay(20f)
assertDuration("2.0 hours")
assertTotalPay("£40.00")
submit()
}
homeScreen {
clickOnItemWithText("Edited this shift")
}
viewScreen {
matchDescription("Edited this shift")
matchDuration("2 Hours 0 Minutes (+ 30 minutes break)")
matchTotalPay("2.0 Hours @ £20.00 per Hour\nEquals: £40.00")
}
}
// filter the list with date from
@Test
fun test3() {
homeScreen {
applySort(Sortable.TYPE, Order.DESCENDING)
clickOnItemAtPosition(0)
viewScreen {
matchDescription("Day five")
matchShiftType(ShiftType.PIECE)
}
}
}
// filter the list with date to
@Test
fun test4() {
homeScreen {
clickFilterInMenu()
}
filterScreen {
setDateIn(2023,8,3)
setDateOut(2023,8,6)
submit()
}
homeScreen {
clickOnItemAtPosition(0)
}
}
// Add a shift as piece rate
@Test
fun test5() {
homeScreen {
clickFab()
}
addScreen {
setDescription("This is a description")
setDate(2023, 2, 11)
clickShiftType(ShiftType.PIECE)
setRateOfPay(10f)
setUnits(1f)
assertTotalPay("£10.00")
submit()
}
homeScreen {
clickOnItemWithText("This is a description")
}
}
// Validate the details screen
@Test
fun test6() {
}
// filter, sort, order and then reset
@Test
fun test7() {
}
}

View File

@@ -0,0 +1 @@
package com.appttude.h_mal.farmr.ui.utils

View File

@@ -0,0 +1,103 @@
package com.appttude.h_mal.farmr.ui.utils
import com.appttude.h_mal.farmr.model.Shift
import com.appttude.h_mal.farmr.model.ShiftType
fun getShifts() = listOf(
Shift(
ShiftType.HOURLY,
"Day one",
"2023-08-01",
"12:00",
"13:00",
1f,
0,
0f,
10f,
10f
),
Shift(
ShiftType.HOURLY,
"Day two",
"2023-08-02",
"12:00",
"13:00",
1f,
0,
0f,
10f,
10f
),
Shift(
ShiftType.HOURLY,
"Day three",
"2023-08-03",
"12:00",
"13:00",
1f,
30,
0f,
10f,
5f
),
Shift(
ShiftType.HOURLY,
"Day four",
"2023-08-04",
"12:00",
"13:00",
1f,
30,
0f,
10f,
5f
),
Shift(
ShiftType.PIECE,
"Day five",
"2023-08-05",
"",
"",
0f,
0,
1f,
10f,
10f
),
Shift(
ShiftType.PIECE,
"Day six",
"2023-08-06",
"",
"",
0f,
0,
1f,
10f,
10f
),
Shift(
ShiftType.PIECE,
"Day seven",
"2023-08-07",
"",
"",
0f,
0,
1f,
10f,
10f
),
Shift(
ShiftType.PIECE,
"Day eight",
"2023-08-08",
"",
"",
0f,
0,
1f,
10f,
10f
)
)

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.farmr.data.ui.utils package com.appttude.h_mal.farmr.ui.utils
import android.os.SystemClock.sleep import android.os.SystemClock.sleep
import android.view.View import android.view.View

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.farmr.data.ui.utils package com.appttude.h_mal.farmr.ui.utils
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer

View File

@@ -0,0 +1,31 @@
package com.appttude.h_mal.farmr.base
import android.app.Application
import com.appttude.h_mal.farmr.data.RepositoryImpl
import com.appttude.h_mal.farmr.data.legacydb.LegacyDatabase
import com.appttude.h_mal.farmr.data.prefs.PreferenceProvider
import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.androidXModule
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.provider
import org.kodein.di.generic.singleton
abstract class BaseApplication() : Application(), KodeinAware {
// Kodein creation of modules to be retrieve within the app
override val kodein = Kodein.lazy {
import(androidXModule(this@BaseApplication))
bind() from singleton { createDatabase() }
bind() from singleton { createPrefs() }
bind() from singleton { RepositoryImpl(instance(), instance()) }
bind() from provider { ApplicationViewModelFactory(instance()) }
}
abstract fun createDatabase(): LegacyDatabase
abstract fun createPrefs(): PreferenceProvider
}

View File

@@ -3,12 +3,13 @@ package com.appttude.h_mal.farmr.data.legacydb
import android.content.ContentResolver import android.content.ContentResolver
import android.net.Uri import android.net.Uri
import android.provider.BaseColumns import android.provider.BaseColumns
import com.appttude.h_mal.farmr.BuildConfig
/** /**
* Created by h_mal on 26/12/2017. * Created by h_mal on 26/12/2017.
*/ */
object ShiftsContract { object ShiftsContract {
const val CONTENT_AUTHORITY = "com.appttude.h_mal.farmr" const val CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID
val BASE_CONTENT_URI = Uri.parse("content://$CONTENT_AUTHORITY") val BASE_CONTENT_URI = Uri.parse("content://$CONTENT_AUTHORITY")
const val PATH_SHIFTS = "shifts" const val PATH_SHIFTS = "shifts"

View File

@@ -62,4 +62,8 @@ class PreferenceProvider(
) )
} }
fun clearPrefs() {
preference.edit().clear().apply()
}
} }

View File

@@ -1,11 +1,10 @@
package com.appttude.h_mal.farmr.di package com.appttude.h_mal.farmr.di
import android.app.Application import com.appttude.h_mal.farmr.base.BaseApplication
import com.appttude.h_mal.farmr.data.RepositoryImpl import com.appttude.h_mal.farmr.data.RepositoryImpl
import com.appttude.h_mal.farmr.data.legacydb.LegacyDatabase import com.appttude.h_mal.farmr.data.legacydb.LegacyDatabase
import com.appttude.h_mal.farmr.data.prefs.PreferenceProvider import com.appttude.h_mal.farmr.data.prefs.PreferenceProvider
import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
import org.kodein.di.Kodein import org.kodein.di.Kodein
import org.kodein.di.KodeinAware import org.kodein.di.KodeinAware
import org.kodein.di.android.x.androidXModule import org.kodein.di.android.x.androidXModule
@@ -14,15 +13,12 @@ import org.kodein.di.generic.instance
import org.kodein.di.generic.provider import org.kodein.di.generic.provider
import org.kodein.di.generic.singleton import org.kodein.di.generic.singleton
class ShiftApplication: Application(), KodeinAware { class ShiftApplication: BaseApplication() {
// Kodein creation of modules to be retrieve within the app
override val kodein = Kodein.lazy {
import(androidXModule(this@ShiftApplication))
bind() from singleton { LegacyDatabase(contentResolver) } override fun createDatabase(): LegacyDatabase {
bind() from singleton { PreferenceProvider(this@ShiftApplication) } return LegacyDatabase(contentResolver)
bind() from singleton { RepositoryImpl(instance(), instance()) }
bind() from provider { ApplicationViewModelFactory(instance()) }
} }
}
override fun createPrefs() = PreferenceProvider(this)
}

View File

@@ -11,5 +11,9 @@ enum class Sortable(val label: String) {
companion object { companion object {
val entries = Sortable.values() val entries = Sortable.values()
fun getEnumByType(label: String): Sortable {
return Sortable.values().first { it.label == label }
}
} }
} }

View File

@@ -18,6 +18,7 @@ import com.appttude.h_mal.farmr.model.Success
import com.appttude.h_mal.farmr.utils.ID import com.appttude.h_mal.farmr.utils.ID
import com.appttude.h_mal.farmr.utils.createDialog import com.appttude.h_mal.farmr.utils.createDialog
import com.appttude.h_mal.farmr.utils.displayToast import com.appttude.h_mal.farmr.utils.displayToast
import com.appttude.h_mal.farmr.utils.formatAsCurrencyString
import com.appttude.h_mal.farmr.utils.formatToTwoDpString import com.appttude.h_mal.farmr.utils.formatToTwoDpString
import com.appttude.h_mal.farmr.utils.hide import com.appttude.h_mal.farmr.utils.hide
import com.appttude.h_mal.farmr.utils.popBackStack import com.appttude.h_mal.farmr.utils.popBackStack
@@ -158,8 +159,8 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
mUnits = units mUnits = units
} }
} }
mPayRateEditText.setText(rateOfPay.formatToTwoDpString()) mPayRateEditText.setText(rateOfPay.formatAsCurrencyString())
mTotalPayTextView.text = totalPay.formatToTwoDpString() mTotalPayTextView.text = totalPay.formatAsCurrencyString()
calculateTotalPay() calculateTotalPay()
} }
@@ -267,7 +268,7 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
(mUnits ?: 0f) * mPayRate (mUnits ?: 0f) * mPayRate
} }
} }
mTotalPayTextView.text = total.formatToTwoDpString() mTotalPayTextView.text = total.formatAsCurrencyString()
} }
} }

View File

@@ -155,7 +155,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
.setSingleChoiceItems( .setSingleChoiceItems(
groupName, groupName,
checkedItem checkedItem
) { p0, p1 -> sort = Sortable.valueOf(groupName[p1]) } ) { p0, p1 -> sort = Sortable.getEnumByType(groupName[p1]) }
.setPositiveButton("Ascending") { dialog, id -> .setPositiveButton("Ascending") { dialog, id ->
viewModel.setSortAndOrder(sort) viewModel.setSortAndOrder(sort)
dialog.dismiss() dialog.dismiss()

View File

@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper
import android.view.View import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
@@ -16,8 +17,7 @@ class SplashScreen : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash) setContentView(R.layout.activity_splash)
val bundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.hyperspace_jump, android.R.anim.fade_out).toBundle()
val relativeLayout = findViewById<View>(R.id.splash_layout) as RelativeLayout
val i = Intent(this@SplashScreen, MainActivity::class.java) val i = Intent(this@SplashScreen, MainActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK) i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK)
Handler().postDelayed({ Handler().postDelayed({
@@ -27,11 +27,11 @@ class SplashScreen : Activity() {
startActivity(i) startActivity(i)
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
// finish(); // finish();
}, SPLASH_TIME_OUT.toLong()) }, SPLASH_TIME_OUT)
} }
companion object { companion object {
// Splash screen timer // Splash screen timer
private const val SPLASH_TIME_OUT = 2000 const val SPLASH_TIME_OUT: Long = 2000
} }
} }