diff --git a/MobileAuthApp/app/build.gradle b/MobileAuthApp/app/build.gradle
index 0d59bc9..8cff52f 100644
--- a/MobileAuthApp/app/build.gradle
+++ b/MobileAuthApp/app/build.gradle
@@ -67,4 +67,7 @@ dependencies {
'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-gson:0.11.2'
+ // Retrofit + Moshi Converter
+ implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
+ implementation 'com.squareup.moshi:moshi-kotlin:1.9.3'
}
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/AndroidManifest.xml b/MobileAuthApp/app/src/main/AndroidManifest.xml
index f592e7e..d13134c 100644
--- a/MobileAuthApp/app/src/main/AndroidManifest.xml
+++ b/MobileAuthApp/app/src/main/AndroidManifest.xml
@@ -19,6 +19,13 @@
+
+
+
+
+
+
+
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt
index 6cf616e..91f0811 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt
@@ -1,7 +1,6 @@
package com.tarkvaraprojekt.mobileauthapp
-import android.app.Activity
-import android.content.Context
+import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.tech.IsoDep
import android.os.Bundle
@@ -10,15 +9,16 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
import com.tarkvaraprojekt.mobileauthapp.auth.Authenticator
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
import java.lang.Exception
-import kotlin.concurrent.thread
/**
* Fragment that asks the user to detect the ID card with mobile NFC chip.
@@ -31,6 +31,8 @@ class AuthFragment : Fragment() {
private var binding: FragmentAuthBinding? = null
+ private val args: CanFragmentArgs by navArgs()
+
private lateinit var timer: CountDownTimer
private var timeRemaining: Int = 90
@@ -69,51 +71,66 @@ class AuthFragment : Fragment() {
}
private fun getInfoFromIdCard(adapter: NfcAdapter) {
- adapter.enableReaderMode(activity, { tag ->
- timer.cancel()
- requireActivity().runOnUiThread {
- binding!!.timeCounter.text = getString(R.string.card_detected)
- }
- val card = IsoDep.get(tag)
- card.timeout = 32768
- card.use {
- try {
- val comms = Comms(it, viewModel.userCan)
- val response = comms.readPersonalData(byteArrayOf(1, 2, 6, 3, 4, 8))
- viewModel.setUserFirstName(response[1])
- viewModel.setUserLastName(response[0])
- viewModel.setUserIdentificationNumber(response[2])
- viewModel.setGender(response[3])
- viewModel.setCitizenship(response[4])
- viewModel.setExpiration(response[5])
- requireActivity().runOnUiThread{
- binding!!.timeCounter.text = getString(R.string.data_read)
- }
- } catch (e: Exception) {
- requireActivity().runOnUiThread {
- binding!!.timeCounter.text = getString(R.string.no_success)
- }
- // If the CAN is wrong we will also delete the saved CAN so that the user won't use it again.
- viewModel.deleteCan(requireContext())
- // Gives user some time to read the error message
- Thread.sleep(1000)
- goToTheStart()
- } finally {
- adapter.disableReaderMode(activity)
+ if (args.reading) {
+ adapter.enableReaderMode(activity, { tag ->
+ timer.cancel()
+ requireActivity().runOnUiThread {
+ binding!!.timeCounter.text = getString(R.string.card_detected)
}
- }
- }, NfcAdapter.FLAG_READER_NFC_A, null)
+ val card = IsoDep.get(tag)
+ card.timeout = 32768
+ card.use {
+ try {
+ val comms = Comms(it, viewModel.userCan)
+ val response = comms.readPersonalData(byteArrayOf(1, 2, 6, 3, 4, 8))
+ viewModel.setUserFirstName(response[1])
+ viewModel.setUserLastName(response[0])
+ viewModel.setUserIdentificationNumber(response[2])
+ viewModel.setGender(response[3])
+ viewModel.setCitizenship(response[4])
+ viewModel.setExpiration(response[5])
+ requireActivity().runOnUiThread {
+ binding!!.timeCounter.text = getString(R.string.data_read)
+ }
+ } catch (e: Exception) {
+ requireActivity().runOnUiThread {
+ binding!!.timeCounter.text = getString(R.string.no_success)
+ }
+ // If the CAN is wrong we will also delete the saved CAN so that the user won't use it again.
+ viewModel.deleteCan(requireContext())
+ // Gives user some time to read the error message
+ Thread.sleep(1000)
+ goToTheStart()
+ } finally {
+ adapter.disableReaderMode(activity)
+ }
+ }
+ }, NfcAdapter.FLAG_READER_NFC_A, null)
+ } else { //We want to create a JWT instead of reading the info from the card.
+ goToNextFragment()
+ }
}
private fun goToNextFragment() {
timer.cancel()
- findNavController().navigate(R.id.action_authFragment_to_userFragment)
+ if (args.auth) {
+ val action = AuthFragmentDirections.actionAuthFragmentToResultFragment(mobile = args.mobile)
+ findNavController().navigate(action)
+ } else {
+ findNavController().navigate(R.id.action_authFragment_to_userFragment)
+ }
}
private fun goToTheStart() {
viewModel.clearUserInfo()
timer.cancel()
- findNavController().navigate(R.id.action_authFragment_to_homeFragment)
+ if (args.reading) {
+ findNavController().navigate(R.id.action_authFragment_to_homeFragment)
+ } else {
+ val resultIntent = Intent()
+ requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
+ requireActivity().finish()
+ }
}
override fun onDestroy() {
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/CanFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/CanFragment.kt
index cdf9e4e..7ad1207 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/CanFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/CanFragment.kt
@@ -1,11 +1,13 @@
package com.tarkvaraprojekt.mobileauthapp
import android.app.AlertDialog
+import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
@@ -65,7 +67,7 @@ class CanFragment : Fragment() {
* Takes user to the next fragment, which is PinFragment.
*/
private fun goToTheNextFragment() {
- val action = CanFragmentDirections.actionCanFragmentToPinFragment(reading = args.reading)
+ val action = CanFragmentDirections.actionCanFragmentToPinFragment(reading = args.reading, auth = args.auth, mobile = args.mobile)
findNavController().navigate(action)
}
@@ -122,8 +124,13 @@ class CanFragment : Fragment() {
* not the HomeFragment.
*/
private fun goToTheStart() {
+ // TODO: Needs special handling when the app is launched with intent. Temporary solution at the moment.
if (args.saving) {
findNavController().navigate(R.id.action_canFragment_to_settingsFragment)
+ } else if (args.auth) {
+ val resultIntent = Intent()
+ requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
+ requireActivity().finish()
} else {
findNavController().navigate(R.id.action_canFragment_to_homeFragment)
}
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/HomeFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/HomeFragment.kt
index 77a2552..e53cd54 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/HomeFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/HomeFragment.kt
@@ -1,6 +1,8 @@
package com.tarkvaraprojekt.mobileauthapp
+import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -37,6 +39,14 @@ class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initialChecks()
+ var mobile = false
+ if (requireActivity().intent.data?.getQueryParameter("arg1") != null) {
+ mobile = true
+ }
+ val auth = requireActivity().intent.getBooleanExtra("auth", false)
+ if (auth || mobile){
+ goToTheNextFragment(true, mobile)
+ }
binding!!.beginButton.setOnClickListener { goToTheNextFragment() }
}
@@ -52,13 +62,19 @@ class HomeFragment : Fragment() {
/**
* Starts the process of interacting with the ID card by sending user to the CAN fragment.
*/
- private fun goToTheNextFragment() {
+ private fun goToTheNextFragment(auth: Boolean = false, mobile: Boolean = false) {
// Making settings menu inactive
(activity as MainActivity).menuAvailable = false
// Currently saving is true because the application is not yet integrated with
// other applications or websites.
- val action = HomeFragmentDirections.actionHomeFragmentToCanFragment(reading = true)
- findNavController().navigate(action)
+ // TODO: Check the navigation action default values. Not everything has to be declared explicitly.
+ if (auth) {
+ val action = HomeFragmentDirections.actionHomeFragmentToCanFragment(reading = false, auth = true, mobile = mobile)
+ findNavController().navigate(action)
+ } else {
+ val action = HomeFragmentDirections.actionHomeFragmentToCanFragment(reading = true, auth = false, mobile = mobile)
+ findNavController().navigate(action)
+ }
}
/**
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/Pin2Fragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/Pin2Fragment.kt
new file mode 100644
index 0000000..189c440
--- /dev/null
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/Pin2Fragment.kt
@@ -0,0 +1,72 @@
+package com.tarkvaraprojekt.mobileauthapp
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentPin2Binding
+import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
+
+/**
+ * Fragment that deals with asking PIN 2 from the user. Basically the same as PIN 1 fragment.
+ */
+class Pin2Fragment : Fragment() {
+
+ private val viewModel: SmartCardViewModel by activityViewModels()
+
+ private var binding: FragmentPin2Binding? = null
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = FragmentPin2Binding.inflate(inflater, container, false)
+ return binding!!.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding!!.nextButton.setOnClickListener {
+ checkPin2Length()
+ }
+ binding!!.cancelButton.setOnClickListener {
+ cancel()
+ }
+ }
+
+ /**
+ * Checks if the length of the entered PIN 2 is in range 5..12 and if it is
+ * then it is saved to the viewModel.
+ */
+ private fun checkPin2Length() {
+ val enteredPin2 = binding!!.pin2EditText.editText?.text.toString()
+ if (enteredPin2.length in 5..12) {
+ viewModel.setUserPin2(enteredPin2)
+ } else {
+ Toast.makeText(requireContext(), getString(R.string.length_pin2), Toast.LENGTH_SHORT)
+ .show()
+ }
+ }
+
+ /**
+ * Authentication process is cancelled when cancel button is clicked and the application
+ * will be closed.
+ */
+ private fun cancel() {
+ val resultIntent = Intent()
+ requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
+ requireActivity().finish()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ binding = null
+ }
+
+}
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/PinFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/PinFragment.kt
index 68b6355..3248ef0 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/PinFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/PinFragment.kt
@@ -1,11 +1,13 @@
package com.tarkvaraprojekt.mobileauthapp
import android.app.AlertDialog
+import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
@@ -68,7 +70,8 @@ class PinFragment : Fragment() {
* Takes user to the next fragment, which is AuthFragment.
*/
private fun goToTheNextFragment() {
- findNavController().navigate(R.id.action_pinFragment_to_authFragment)
+ val action = PinFragmentDirections.actionPinFragmentToAuthFragment(reading = args.reading, auth = args.auth, mobile = args.mobile)
+ findNavController().navigate(action)
}
/**
@@ -124,10 +127,13 @@ class PinFragment : Fragment() {
*/
private fun goToTheStart() {
if (args.saving) {
- findNavController().navigate(R.id.action_pinFragment_to_settingsFragment)
+ findNavController().navigate(R.id.action_canFragment_to_settingsFragment)
+ } else if (args.auth) {
+ val resultIntent = Intent()
+ requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
+ requireActivity().finish()
} else {
- viewModel.clearUserInfo()
- findNavController().navigate(R.id.action_pinFragment_to_homeFragment)
+ findNavController().navigate(R.id.action_canFragment_to_homeFragment)
}
}
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/ResultFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/ResultFragment.kt
new file mode 100644
index 0000000..9d01c8f
--- /dev/null
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/ResultFragment.kt
@@ -0,0 +1,58 @@
+package com.tarkvaraprojekt.mobileauthapp
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.navigation.fragment.navArgs
+import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentResultBinding
+import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
+
+/**
+ * ResultFragment is used to create a JWT and to send response to the website/application
+ * that launched the MobileAuthApp. If the mobile auth app was started by a website
+ * the result is sent to a server with a POST request.
+ */
+class ResultFragment : Fragment() {
+
+ private val viewModel: SmartCardViewModel by activityViewModels()
+
+ private var binding: FragmentResultBinding? = null
+
+ private val args: CanFragmentArgs by navArgs()
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = FragmentResultBinding.inflate(inflater, container, false)
+ return binding!!.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding!!.resultBackButton.setOnClickListener {
+ if (!args.mobile) {
+ createResponse()
+ }
+ }
+ }
+
+ private fun createResponse() {
+ val resultIntent = Intent()
+ requireActivity().setResult(AppCompatActivity.RESULT_OK, resultIntent)
+ requireActivity().finish()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ binding = null
+ }
+
+}
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/model/SmartCardViewModel.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/model/SmartCardViewModel.kt
index 1b66115..8780736 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/model/SmartCardViewModel.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/model/SmartCardViewModel.kt
@@ -11,6 +11,9 @@ class SmartCardViewModel: ViewModel() {
private var _userPin: String = ""
val userPin get() = _userPin
+ private var _userPin2: String = ""
+ val userPin2 get() = _userPin2
+
private var _userCan: String = ""
val userCan get() = _userCan
@@ -34,6 +37,7 @@ class SmartCardViewModel: ViewModel() {
fun clearUserInfo() {
_userPin = ""
+ _userPin2 = ""
_userCan = ""
_userFirstName = ""
_userLastName = ""
@@ -47,6 +51,10 @@ class SmartCardViewModel: ViewModel() {
_userPin = newUserPin
}
+ fun setUserPin2(newUserPin2: String) {
+ _userPin2 = newUserPin2
+ }
+
fun setUserCan(newUserCan: String) {
_userCan = newUserCan
}
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/network/Response.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/network/Response.kt
new file mode 100644
index 0000000..199b913
--- /dev/null
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/network/Response.kt
@@ -0,0 +1,9 @@
+package com.tarkvaraprojekt.mobileauthapp.network
+
+/**
+ * Placeholder ResponseItem.
+ */
+data class ResponseItem (
+ val data1: Int,
+ val data2: Int,
+)
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/network/TokenApiService.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/network/TokenApiService.kt
new file mode 100644
index 0000000..aa08490
--- /dev/null
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/network/TokenApiService.kt
@@ -0,0 +1,37 @@
+package com.tarkvaraprojekt.mobileauthapp.network
+
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import retrofit2.Response
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.http.Body
+import retrofit2.http.GET
+import retrofit2.http.Headers
+import retrofit2.http.POST
+
+/**
+ * Class for making HTTP requests
+ * Based on https://developer.android.com/courses/pathways/android-basics-kotlin-unit-4-pathway-2
+ */
+private const val BASE_URL =
+ "add-endpoint-url-here"
+
+private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
+private val retrofit = Retrofit.Builder().addConverterFactory(MoshiConverterFactory.create(moshi))
+ .baseUrl(BASE_URL).build()
+
+interface TokenApiService {
+ @GET("something")
+ suspend fun getData(): ResponseItem
+
+ @Headers("Content-Type: application/json")
+ @POST("posts")
+ suspend fun addData(@Body data: ResponseItem): Response
+}
+
+object TokenApi {
+ val retrofitService : TokenApiService by lazy {
+ retrofit.create(TokenApiService::class.java)
+ }
+}
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/res/layout/fragment_pin2.xml b/MobileAuthApp/app/src/main/res/layout/fragment_pin2.xml
new file mode 100644
index 0000000..8929989
--- /dev/null
+++ b/MobileAuthApp/app/src/main/res/layout/fragment_pin2.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/res/layout/fragment_result.xml b/MobileAuthApp/app/src/main/res/layout/fragment_result.xml
new file mode 100644
index 0000000..1bff4c1
--- /dev/null
+++ b/MobileAuthApp/app/src/main/res/layout/fragment_result.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/res/navigation/nav_graph.xml b/MobileAuthApp/app/src/main/res/navigation/nav_graph.xml
index 39fe753..e981297 100644
--- a/MobileAuthApp/app/src/main/res/navigation/nav_graph.xml
+++ b/MobileAuthApp/app/src/main/res/navigation/nav_graph.xml
@@ -46,6 +46,14 @@
android:name="reading"
app:argType="boolean"
android:defaultValue="false" />
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/res/values-en/strings.xml b/MobileAuthApp/app/src/main/res/values-en/strings.xml
index 8572c9c..7aa4439 100644
--- a/MobileAuthApp/app/src/main/res/values-en/strings.xml
+++ b/MobileAuthApp/app/src/main/res/values-en/strings.xml
@@ -5,64 +5,74 @@
Work in progress
- PIN 1 on salvestatud
- PIN 1 ei ole salvestatud
- CAN on salvestatud
- CAN ei ole salvestatud
+ PIN 1 saved
+ PIN 1 not saved
+ CAN saved
+ CAN not saved
- LOE ID KAARTI
- EDASI
- KATKESTA
- SALVESTA
- EI
- TAGASI
+ READ ID CARD
+ NEXT
+ CANCEL
+ SAVE
+ NO
+ BACK
- Palun sisesta PIN 1
+ Please enter PIN 1PIN 1
- Näide. 1234
- PIN 1 lubatud pikkus on 4..12
- Praegu ei ole rakenduses PIN 1 salvestatud. Kas sa soovid sisestatud PIN 1-te salvestada? Sellisel juhul sisestatakse see järgmisel korral automaatselt. Salvestatud PIN 1-te saab alati menüüs muuta ja kustutada.
- Salvesta PIN 1
+ Example. 1234
+ Allowed length for PIN 1 is 4..12
+ PIN 1 is currently not saved. Do you wish to save the entered PIN 1? Saved PIN 1 will be entered automatically in the future. Saved PIN 1 can be changed and deleted in the settings menu.
+ Save PIN 1
+
+
+ Please enter PIN 2
+ PIN 2
+ Example. 123456
+ Allowed length for PIN 2 is 5..12
- Näide. 123456
+ Example. 123456CAN
- Sisesta ID kaardi CAN (Card Access Number)
- CANi pikkus on vale
- Kaart on tuvastatud. Hoia kaarti vastu telefoni.
- Andmed loetud. Võid edasi minna.
- Praegu ei ole rakenduses CAN salvestatud. Kas sa soovid sisestatud CANi salvestada? Sellisel juhul sisestatakse see järgmisel korral automaatselt. Salvestatud CANi saab alati menüüs muuta ja kustutada.
- Salvesta CAN
+ Enter ID card\'s CAN (Card Access Number)
+ Length of the CAN is wrong
+ Card detected. Hold it against the phone.
+ Data read. You can continue.
+ Save CAN
- ID kaardiga ühenduse loomiseks pane kaart vastu telefoni
- Aega on jäänud %d sek
- Aeg on otsas
- Vale CAN
+ Put the ID card against the phone to establish connection
+ Time left %d sek
+ No time left
+ Wrong CAN
- NIMI
+ NAME%1$s %2$s
- ISIKUKOOD
- KEHTIV KUNI
- KODAKONDSUS
- SUGU
- UNUSTA
+ IDENTIFICATION NUMBER
+ DATE OF EXPIRY
+ CITIZENSHIP
+ SEX
+ FORGET
+
+
+ See Fragment vastutab vastuse tagastamise eest.
+ Hiljem sulgeb rakendus automaatselt.
- Seaded
- Keel
- Toiming pole hetkel saadaval
+ Settings
+ Language
+ Currently unavailableCAN: %s
- Lisa CAN
- Kustuta CAN
+ Add CAN
+ Delete CANPIN1: %s
- Lisa PIN1
- Kustuta PIN1
- puudub
- NÄITA
- PEIDA
+ Add PIN1
+ Delete PIN1
+ not saved
+ SHOW
+ HIDE****
- Seaded pole hetkel saadaval
+ Settings currently unavailabe
+ CAN is currently not saved. Do you wish to save the CAN? Saved CAN will be entered automatically in the future. Saved CAN can be changed and deleted in the settings menu.
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/res/values-et/strings.xml b/MobileAuthApp/app/src/main/res/values-et/strings.xml
index 676b5c9..66afcfb 100644
--- a/MobileAuthApp/app/src/main/res/values-et/strings.xml
+++ b/MobileAuthApp/app/src/main/res/values-et/strings.xml
@@ -24,6 +24,12 @@
Praegu ei ole rakenduses PIN 1 salvestatud. Kas sa soovid sisestatud PIN 1-te salvestada? Sellisel juhul sisestatakse see järgmisel korral automaatselt. Salvestatud PIN 1-te saab alati menüüs muuta ja kustutada.Salvesta PIN 1
+
+ Palun sisesta PIN 2
+ PIN 2
+ Näide. 123456
+ PIN 2 lubatud pikkus on 5..12
+
Näide. 123456CAN
@@ -48,6 +54,10 @@
KODAKONDSUSSUGU
+
+ See Fragment vastutab vastuse tagastamise eest.
+ Hiljem sulgeb rakendus automaatselt.
+
SeadedKeel
diff --git a/MobileAuthApp/app/src/main/res/values/strings.xml b/MobileAuthApp/app/src/main/res/values/strings.xml
index 26fddf6..0a39206 100644
--- a/MobileAuthApp/app/src/main/res/values/strings.xml
+++ b/MobileAuthApp/app/src/main/res/values/strings.xml
@@ -2,65 +2,75 @@
NFC authenticationWork in progress
- LOE ID KAARTI
- EDASI
- KATKESTA
- SALVESTA
- EI
- TAGASI
+ READ ID CARD
+ NEXT
+ CANCEL
+ SAVE
+ NO
+ BACK
- PIN 1 on salvestatud
- PIN 1 ei ole salvestatud
- CAN on salvestatud
- CAN ei ole salvestatud
+ PIN 1 saved
+ PIN 1 not saved
+ CAN saved
+ CAN not saved
- Palun sisesta PIN 1
+ Please enter PIN 1PIN 1
- Näide. 1234
- PIN 1 lubatud pikkus on 4..12
- Praegu ei ole rakenduses PIN 1 salvestatud. Kas sa soovid sisestatud PIN 1-te salvestada? Sellisel juhul sisestatakse see järgmisel korral automaatselt. Salvestatud PIN 1-te saab alati menüüs muuta ja kustutada.
- Salvesta PIN 1
+ Example. 1234
+ Allowed length for PIN 1 is 4..12
+ PIN 1 is currently not saved. Do you wish to save the entered PIN 1? Saved PIN 1 will be entered automatically in the future. Saved PIN 1 can be changed and deleted in the settings menu.
+ Save PIN 1
+
+
+ Please enter PIN 2
+ PIN 2
+ Example. 123456
+ Allowed length for PIN 2 is 5..12
- Näide. 123456
+ Example. 123456CAN
- Sisesta ID kaardi CAN (Card Access Number)
- CANi pikkus on vale
- Kaart on tuvastatud. Hoia kaarti vastu telefoni.
- Andmed loetud. Võid edasi minna.
- Praegu ei ole rakenduses CAN salvestatud. Kas sa soovid sisestatud CANi salvestada? Sellisel juhul sisestatakse see järgmisel korral automaatselt. Salvestatud CANi saab alati menüüs muuta ja kustutada.
- Salvesta CAN
+ Enter ID card\'s CAN (Card Access Number)
+ Length of the CAN is wrong
+ Card detected. Hold it against the phone.
+ Data read. You can continue.
+ CAN is currently not saved. Do you wish to save the CAN? Saved CAN will be entered automatically in the future. Saved CAN can be changed and deleted in the settings menu.
+ Save CAN
- ID kaardiga ühenduse loomiseks pane kaart vastu telefoni
- Aega on jäänud %d sek
- Aeg on otsas
- Vale CAN
+ Put the ID card against the phone to establish connection
+ Time left %d sek
+ No time left
+ Wrong CAN
- NIMI
+ NAME%1$s %2$s
- ISIKUKOOD
- KEHTIV KUNI
- KODAKONDSUS
- SUGU
- UNUSTA
+ IDENTIFICATION NUMBER
+ DATE OF EXPIRY
+ CITIZENSHIP
+ SEX
+ FORGET
+
+
+ See Fragment vastutab vastuse tagastamise eest.
+ Hiljem sulgeb rakendus automaatselt.
- Seaded
- Keel
- Toiming pole hetkel saadaval
+ Settings
+ Language
+ Currently unavailableCAN: %s
- Lisa CAN
- Kustuta CAN
+ Add CAN
+ Delete CANPIN1: %s
- Lisa PIN 1
- Kustuta PIN 1
- puudub
- NÄITA
- PEIDA
+ Add PIN 1
+ Delete PIN 1
+ not saved
+ SHOW
+ HIDE****
- Seaded pole hetkel saadaval
+ Settings currently unavailable
\ No newline at end of file
diff --git a/TestMobileApp/.gitignore b/TestMobileApp/.gitignore
new file mode 100644
index 0000000..565a541
--- /dev/null
+++ b/TestMobileApp/.gitignore
@@ -0,0 +1,16 @@
+*.iml
+.gradle
+.idea
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/TestMobileApp/app/.gitignore b/TestMobileApp/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/TestMobileApp/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/TestMobileApp/app/build.gradle b/TestMobileApp/app/build.gradle
new file mode 100644
index 0000000..4645a41
--- /dev/null
+++ b/TestMobileApp/app/build.gradle
@@ -0,0 +1,46 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdk 31
+
+ defaultConfig {
+ applicationId "com.example.testmobileapp"
+ minSdk 26
+ targetSdk 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ buildFeatures {
+ viewBinding true
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation 'com.koushikdutta.ion:ion:3.1.0'
+ implementation 'androidx.core:core-ktx:1.6.0'
+ implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
\ No newline at end of file
diff --git a/TestMobileApp/app/proguard-rules.pro b/TestMobileApp/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/TestMobileApp/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/TestMobileApp/app/src/androidTest/java/com/example/testmobileapp/ExampleInstrumentedTest.kt b/TestMobileApp/app/src/androidTest/java/com/example/testmobileapp/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..3305cf1
--- /dev/null
+++ b/TestMobileApp/app/src/androidTest/java/com/example/testmobileapp/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.testmobileapp
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.testmobileapp", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/AndroidManifest.xml b/TestMobileApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2fe204c
--- /dev/null
+++ b/TestMobileApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/java/com/example/testmobileapp/MainActivity.kt b/TestMobileApp/app/src/main/java/com/example/testmobileapp/MainActivity.kt
new file mode 100644
index 0000000..8d9e9e0
--- /dev/null
+++ b/TestMobileApp/app/src/main/java/com/example/testmobileapp/MainActivity.kt
@@ -0,0 +1,70 @@
+package com.example.testmobileapp
+
+import android.app.Activity
+import android.content.Intent
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import com.example.testmobileapp.databinding.ActivityMainBinding
+import com.koushikdutta.ion.Ion
+
+/**
+ * Test mobile app to demonstrate how other applications can use MobileAuthApp.
+ * Single purpose app that launches the MobileAuthApp and gets the response back (JWT).
+ */
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var authLauncher: ActivityResultLauncher
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ authLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { response ->
+ if (response.resultCode == Activity.RESULT_OK) {
+ // Currently we are not actually checking whether we get a valid token.
+ // For testing purposes only, to make sure that we are able to get a response at all.
+ binding.loginTextView.text = getString(R.string.auth_success)
+ }
+ if (response.resultCode == Activity.RESULT_CANCELED) {
+ binding.loginTextView.text = getString(R.string.auth_failure)
+ }
+ }
+
+ binding.loginOptionNfcButton.setOnClickListener { launchAuth() }
+
+ }
+
+ /**
+ * Method that creates an intent to launch the MobileAuthApp
+ */
+ private fun launchAuth(arg: String = "nothing") {
+ val launchIntent = Intent()
+ launchIntent.setClassName("com.tarkvaraprojekt.mobileauthapp", "com.tarkvaraprojekt.mobileauthapp.MainActivity")
+ launchIntent.putExtra("auth", true)
+ launchIntent.putExtra("nonce", arg) // Currently nothing
+ authLauncher.launch(launchIntent)
+ }
+
+ /**
+ * Method for retrieving data from an endpoint.
+ * Ion library is used as it is very convenient for making simple GET requests.
+ */
+ private fun getData() {
+ val url = "real-address-here"
+ Ion.with(applicationContext)
+ .load(url)
+ .asJsonObject()
+ .setCallback { _, result ->
+ try {
+ // Get data from the result and call launchAuth method
+ launchAuth()
+ } catch (e: Exception) {
+ Log.i("GETrequest", "was unsuccessful")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/TestMobileApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/drawable/ic_launcher_background.xml b/TestMobileApp/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TestMobileApp/app/src/main/res/layout/activity_main.xml b/TestMobileApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..33cefac
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/TestMobileApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/TestMobileApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/TestMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/TestMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/TestMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/TestMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/TestMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/TestMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/TestMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/TestMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/TestMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/TestMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/TestMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/TestMobileApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/TestMobileApp/app/src/main/res/values-en/strings.xml b/TestMobileApp/app/src/main/res/values-en/strings.xml
new file mode 100644
index 0000000..86688c5
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/values-en/strings.xml
@@ -0,0 +1,9 @@
+
+
+ TestMobileApp
+ Login
+ Choose login method
+ NFC auth
+ Successful response
+ Response failed
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/values-et/strings.xml b/TestMobileApp/app/src/main/res/values-et/strings.xml
new file mode 100644
index 0000000..0daf8b4
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/values-et/strings.xml
@@ -0,0 +1,9 @@
+
+
+ TestMobileApp
+ Logi sisse
+ Vali sobiv meetod
+ NFC auth
+ Vastus kätte saadud
+ Vastust ei õnnestunud kätte saada
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/values-night/themes.xml b/TestMobileApp/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..8ad2788
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/values/colors.xml b/TestMobileApp/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..7c20a9c
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/values/colors.xml
@@ -0,0 +1,17 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
+
+ #ef9a9a
+ #f44336
+ #d32f2f
+ #ffe082
+ #ffa000
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/values/strings.xml b/TestMobileApp/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2353f0b
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ TestMobileApp
+ Login
+ Choose login method
+ NFC auth
+ Successful response
+ Response failed
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/main/res/values/themes.xml b/TestMobileApp/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..ec1e95c
--- /dev/null
+++ b/TestMobileApp/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/TestMobileApp/app/src/test/java/com/example/testmobileapp/ExampleUnitTest.kt b/TestMobileApp/app/src/test/java/com/example/testmobileapp/ExampleUnitTest.kt
new file mode 100644
index 0000000..791e7f2
--- /dev/null
+++ b/TestMobileApp/app/src/test/java/com/example/testmobileapp/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.testmobileapp
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/TestMobileApp/build.gradle b/TestMobileApp/build.gradle
new file mode 100644
index 0000000..59a783a
--- /dev/null
+++ b/TestMobileApp/build.gradle
@@ -0,0 +1,18 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:7.0.2"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/TestMobileApp/gradle.properties b/TestMobileApp/gradle.properties
new file mode 100644
index 0000000..98bed16
--- /dev/null
+++ b/TestMobileApp/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/TestMobileApp/gradle/wrapper/gradle-wrapper.jar b/TestMobileApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/TestMobileApp/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/TestMobileApp/gradle/wrapper/gradle-wrapper.properties b/TestMobileApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..951629b
--- /dev/null
+++ b/TestMobileApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Oct 26 10:33:23 EEST 2021
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/TestMobileApp/gradlew b/TestMobileApp/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/TestMobileApp/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/TestMobileApp/gradlew.bat b/TestMobileApp/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/TestMobileApp/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/TestMobileApp/settings.gradle b/TestMobileApp/settings.gradle
new file mode 100644
index 0000000..011b30c
--- /dev/null
+++ b/TestMobileApp/settings.gradle
@@ -0,0 +1,10 @@
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ jcenter() // Warning: this repository is going to shut down soon
+ }
+}
+rootProject.name = "TestMobileApp"
+include ':app'