diff --git a/MobileAuthApp/app/build.gradle b/MobileAuthApp/app/build.gradle
index 6f6625a..5cd7f6a 100644
--- a/MobileAuthApp/app/build.gradle
+++ b/MobileAuthApp/app/build.gradle
@@ -59,4 +59,7 @@ dependencies {
//For cryptography
implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69'
+ //SecureDataStoring
+ implementation("androidx.security:security-crypto:1.0.0")
+
}
\ No newline at end of file
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 e918c64..ab14a76 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt
@@ -78,10 +78,13 @@ class AuthFragment : Fragment() {
card.use {
try {
val comms = Comms(it, viewModel.userCan)
- val response = comms.readPersonalData(byteArrayOf(1, 2, 6))
+ 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)
}
@@ -89,6 +92,8 @@ class AuthFragment : Fragment() {
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()
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 2bc8e90..cdf9e4e 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/CanFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/CanFragment.kt
@@ -1,5 +1,6 @@
package com.tarkvaraprojekt.mobileauthapp
+import android.app.AlertDialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -8,11 +9,14 @@ import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentCanBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
/**
- * Fragment that deals with asking the user for six digit CAN
+ * Fragment that deals with asking the user for a six digit CAN. If the CAN is already saved
+ * then the fragment is skipped immediately and if the CAN is not saved then the user
+ * is asked whether it should be saved for the future or not before continuing.
*/
class CanFragment : Fragment() {
@@ -20,6 +24,12 @@ class CanFragment : Fragment() {
private var binding: FragmentCanBinding? = null
+ // Navigation arguments:
+ // saving = true means that we are navigating here from the settings menu and must return to the settings menu.
+ // reading = true means that we are only reading the information from the ID card that does not need PIN 1,
+ // this information is passed on to the next PinFragment.
+ private val args: CanFragmentArgs by navArgs()
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -31,27 +41,92 @@ class CanFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
- binding!!.nextButton.setOnClickListener { goToNextFragment() }
+ checkIfSkip()
+ // If the user arrives from the settings menu then the button should say
+ // save instead of continue.
+ if (args.saving) {
+ binding!!.nextButton.text = getString(R.string.save_text)
+ }
+ binding!!.nextButton.setOnClickListener { checkEnteredCan() }
binding!!.cancelButton.setOnClickListener { goToTheStart() }
}
- private fun goToNextFragment() {
- val enteredCan = binding!!.canEditText.editText?.text.toString()
- if (enteredCan.length != 6) {
- Toast.makeText(requireContext(), getString(R.string.length_can), Toast.LENGTH_SHORT)
- .show()
- } else {
- viewModel.setUserCan(
- binding!!.canEditText.editText?.text.toString()
- )
- findNavController().navigate(R.id.action_canFragment_to_authFragment)
+ /**
+ * Checks if the current fragment can be skipped or not.
+ * If the user has CAN saved on the device there is no need to ask it again.
+ */
+ private fun checkIfSkip() {
+ if (viewModel.userCan.length == 6) {
+ goToTheNextFragment()
}
}
+ /**
+ * Takes user to the next fragment, which is PinFragment.
+ */
+ private fun goToTheNextFragment() {
+ val action = CanFragmentDirections.actionCanFragmentToPinFragment(reading = args.reading)
+ findNavController().navigate(action)
+ }
+
+ /**
+ * Checks whether the user has entered a 6 digit can to the input field.
+ * If yes then the user is allowed to continue otherwise the user is
+ * allowed to modify the entered can.
+ */
+ private fun checkEnteredCan() {
+ val enteredCan = binding!!.canEditText.editText?.text.toString()
+ if (enteredCan.length == 6) {
+ viewModel.setUserCan(enteredCan)
+ if (args.saving) {
+ viewModel.storeCan(requireContext())
+ goToTheStart()
+ } else {
+ val storeCanQuestion = getDialog()
+ storeCanQuestion?.show()
+ }
+ } else {
+ Toast.makeText(requireContext(), getString(R.string.length_can), Toast.LENGTH_SHORT)
+ .show()
+ }
+ }
+
+ /**
+ * Builds a dialog that asks the user whether the entered CAN should be saved
+ * on the device or not.
+ */
+ private fun getDialog(): AlertDialog? {
+ return activity?.let { frag ->
+ val builder = AlertDialog.Builder(frag)
+ builder.apply {
+ // If response is positive then save the CAN on the device.
+ setPositiveButton(R.string.save_text) { _, _ ->
+ viewModel.storeCan(
+ requireContext()
+ )
+ goToTheNextFragment()
+ }
+ setNegativeButton(R.string.deny_text) { _, _ ->
+ goToTheNextFragment()
+ }
+ }
+ builder.setMessage(R.string.can_save_request)
+ builder.setTitle(R.string.save_can_title)
+ builder.create()
+ }
+ }
+
+ /**
+ * Navigates the user back to the start depending on where the user arrived.
+ * If the user arrived from the settings menu then the start is the settings menu
+ * not the HomeFragment.
+ */
private fun goToTheStart() {
- viewModel.clearUserInfo()
- findNavController().navigate(R.id.action_canFragment_to_homeFragment)
+ if (args.saving) {
+ findNavController().navigate(R.id.action_canFragment_to_settingsFragment)
+ } else {
+ findNavController().navigate(R.id.action_canFragment_to_homeFragment)
+ }
}
override fun onDestroy() {
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 bdfaa69..77a2552 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/HomeFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/HomeFragment.kt
@@ -10,6 +10,13 @@ import androidx.navigation.fragment.findNavController
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentHomeBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
+/**
+ * HomeFragment is only shown to the user when then the user launches the application. When the application
+ * is launched by another application or a website then this Fragment will be skipped.
+ * This fragment uses the fields from the MainActivity by casting the activity to MainActivity.
+ * This might not be the best practice, but the application uses a single activity design so it should
+ * always work.
+ */
class HomeFragment : Fragment() {
private val viewModel: SmartCardViewModel by activityViewModels()
@@ -22,17 +29,72 @@ class HomeFragment : Fragment() {
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
+ // Making settings menu active again
+ (activity as MainActivity).menuAvailable = true
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
- binding!!.beginButton.setOnClickListener { goToNextFragment() }
+ initialChecks()
+ binding!!.beginButton.setOnClickListener { goToTheNextFragment() }
}
- private fun goToNextFragment() {
- findNavController().navigate(R.id.action_homeFragment_to_pinFragment)
+ /**
+ * Method where all the initial checks that should be done before any user input is accepted should be added.
+ */
+ private fun initialChecks() {
+ viewModel.checkCan(requireContext())
+ viewModel.checkPin(requireContext())
+ displayStates()
+ }
+
+ /**
+ * Starts the process of interacting with the ID card by sending user to the CAN fragment.
+ */
+ private fun goToTheNextFragment() {
+ // 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)
+ }
+
+ /**
+ * Displays texts that inform the user whether the CAN and PIN 1 are saved on the device or not.
+ * This might help the user to save some time as checking menu is not necessary unless the user
+ * wishes to make changes to the saved CAN or PIN 1.
+ */
+ private fun displayStates() {
+ canState()
+ pinState()
+ }
+
+ /**
+ * Checks the state of the CAN, saved or not saved. Updates the text and logo.
+ */
+ private fun canState() {
+ if (viewModel.userCan.length == 6) {
+ binding!!.canStatusText.text = getString(R.string.can_status_saved)
+ binding!!.canStatusLogo.setImageResource(R.drawable.ic_check_logo)
+ } else {
+ binding!!.canStatusText.text = getString(R.string.can_status_negative)
+ binding!!.canStatusLogo.setImageResource(R.drawable.ic_info_logo)
+ }
+ }
+
+ /**
+ * Checks the state of the PIN 1, saved or not saved. Updates the text and logo.
+ */
+ private fun pinState() {
+ if (viewModel.userPin.length in 4..12) {
+ binding!!.pinStatusText.text = getString(R.string.pin_status_saved)
+ binding!!.pinStatusLogo.setImageResource(R.drawable.ic_check_logo)
+ } else {
+ binding!!.pinStatusText.text = getString(R.string.pin_status_negative)
+ binding!!.pinStatusLogo.setImageResource(R.drawable.ic_info_logo)
+ }
}
override fun onDestroyView() {
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/MainActivity.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/MainActivity.kt
index bc93c57..638d88e 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/MainActivity.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/MainActivity.kt
@@ -1,24 +1,51 @@
package com.tarkvaraprojekt.mobileauthapp
-import android.nfc.NfcAdapter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
-import android.util.Log
+import android.view.Menu
+import android.view.MenuItem
+import android.widget.Toast
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import com.tarkvaraprojekt.mobileauthapp.databinding.ActivityMainBinding
+
+/**
+ * The only activity of the application (single activity design).
+ */
class MainActivity : AppCompatActivity() {
private lateinit var navigationController: NavController
+ // If true the settings menu can be accessed from the toolbar in the upper part of the screen.
+ var menuAvailable: Boolean = true
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
+ setSupportActionBar(binding.toolbar)
+
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navigationController = navHostFragment.navController
}
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.menu, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
+ R.id.menu_settings_option -> {
+ if (menuAvailable) {
+ navigationController.navigate(R.id.action_homeFragment_to_settingsFragment)
+ true
+ } else {
+ Toast.makeText(this, getString(R.string.unavailable), Toast.LENGTH_SHORT).show()
+ false
+ }
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
}
\ 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 23ed504..68b6355 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/PinFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/PinFragment.kt
@@ -1,5 +1,6 @@
package com.tarkvaraprojekt.mobileauthapp
+import android.app.AlertDialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -8,11 +9,14 @@ import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentPinBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
/**
- * Fragment that deals with asking the user for PIN1
+ * Fragment that deals with asking the user for PIN 1. If the user has already saved the PIN 1 then it is not asked again
+ * and the fragment is skipped and if the PIN 1 is not saved then the user is asked whether it should be saved or
+ * not before continuing.
*/
class PinFragment : Fragment() {
@@ -20,6 +24,12 @@ class PinFragment : Fragment() {
private var binding: FragmentPinBinding? = null
+ // Navigation arguments:
+ // saving = true means that the user must be returned to the settings menu
+ // reading = true means that we are reading information from the ID card that does
+ // not require PIN 1 so it is not necessary to ask it.
+ private val args: CanFragmentArgs by navArgs()
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -31,33 +41,94 @@ class PinFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
- binding!!.nextButton.setOnClickListener { goToNextFragment() }
+ checkIfSkip()
+ // If the user arrives from the settings menu then the button says
+ // save instead of continue.
+ if (args.saving) {
+ binding!!.nextButton.text = getString(R.string.save_text)
+ }
+ binding!!.nextButton.setOnClickListener { checkEnteredPin() }
binding!!.cancelButton.setOnClickListener { goToTheStart() }
- // Currently PIN 1 is not required and thus this step is immediately skipped.
- // In the future the UI flow will be changed in the nav_graph.
- goToNextFragment()
}
- private fun goToNextFragment() {
- val enteredPin1 = binding!!.pinEditText.editText?.text.toString()
- if (enteredPin1.length in 4..12) {
- viewModel.setUserPin(
- binding!!.pinEditText.editText?.text.toString()
- )
- findNavController().navigate(R.id.action_pinFragment_to_canFragment)
- } else {
- // Currently it is not important to enter PIN1 so we will allow the user to leave this field empty
- //Toast.makeText(requireContext(), getString(R.string.length_pin), Toast.LENGTH_SHORT)
- // .show()
- viewModel.setUserPin("1234")
- findNavController().navigate(R.id.action_pinFragment_to_canFragment)
+ /**
+ * Checks if the current fragment can be skipped or not.
+ * If the user has PIN 1 saved on the device or PIN 1 is not required
+ * then the PIN 1 won't be asked.
+ */
+ private fun checkIfSkip() {
+ if (args.reading) {
+ goToTheNextFragment()
+ } else if (viewModel.userPin.length in 4..12) {
+ goToTheNextFragment()
}
}
+ /**
+ * Takes user to the next fragment, which is AuthFragment.
+ */
+ private fun goToTheNextFragment() {
+ findNavController().navigate(R.id.action_pinFragment_to_authFragment)
+ }
+
+ /**
+ * Checks whether the user has entered a PIN 1 with length between [4, 12] in the
+ * input field. If yes then the user is allowed to continue otherwise the user is
+ * allowed to modify the entered PIN 1.
+ */
+ private fun checkEnteredPin() {
+ val enteredPin = binding!!.pinEditText.editText?.text.toString()
+ if (enteredPin.length in 4..12) {
+ viewModel.setUserPin(enteredPin)
+ if (args.saving) {
+ viewModel.storePin(requireContext())
+ goToTheStart()
+ } else {
+ val storePinQuestion = getDialog()
+ storePinQuestion?.show()
+ }
+ } else {
+ Toast.makeText(requireContext(), getString(R.string.length_pin), Toast.LENGTH_SHORT)
+ .show()
+ }
+ }
+
+ /**
+ * Builds a dialog that asks the user whether the entered PIN 1 should be saved
+ * on the device or not.
+ */
+ private fun getDialog(): AlertDialog? {
+ return activity?.let { frag ->
+ val builder = AlertDialog.Builder(frag)
+ builder.apply {
+ // If response is positive save the PIN 1 on the device.
+ setPositiveButton(R.string.save_text) { _, _ ->
+ viewModel.storePin(
+ requireContext()
+ )
+ goToTheNextFragment()
+ }
+ setNegativeButton(R.string.deny_text) { _, _ ->
+ goToTheNextFragment()
+ }
+ }
+ builder.setMessage(R.string.pin_save_request)
+ builder.setTitle(R.string.save_pin_title)
+ builder.create()
+ }
+ }
+
+ /**
+ * Returns user to the start. If the user arrived from the settings menu then the start is
+ * settings menu not the HomeFragment.
+ */
private fun goToTheStart() {
- viewModel.clearUserInfo()
- findNavController().navigate(R.id.action_pinFragment_to_homeFragment)
+ if (args.saving) {
+ findNavController().navigate(R.id.action_pinFragment_to_settingsFragment)
+ } else {
+ viewModel.clearUserInfo()
+ findNavController().navigate(R.id.action_pinFragment_to_homeFragment)
+ }
}
override fun onDestroy() {
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/UserFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/UserFragment.kt
index 632e1fa..706fdd2 100644
--- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/UserFragment.kt
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/UserFragment.kt
@@ -33,13 +33,25 @@ class UserFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
- binding!!.userName.text =
- getString(R.string.user_name, viewModel.userFirstName, viewModel.userLastName)
- binding!!.identificationNumber.text = viewModel.userIdentificationNumber
+ displayInformation()
binding!!.clearButton.setOnClickListener { goToTheStart() }
}
+ /**
+ * Assigns text values to the fields in order to display user information.
+ */
+ private fun displayInformation() {
+ binding!!.userName.text =
+ getString(R.string.user_name, viewModel.userFirstName, viewModel.userLastName)
+ binding!!.identificationNumber.text = viewModel.userIdentificationNumber
+ binding!!.gender.text = viewModel.gender
+ binding!!.expiration.text = viewModel.expiration.replace(" ", "/")
+ binding!!.citizenship.text = viewModel.citizenship
+ }
+
+ /**
+ * Navigates user back to the start and also deletes any temporary information.
+ */
private fun goToTheStart() {
viewModel.clearUserInfo()
findNavController().navigate(R.id.action_userFragment_to_homeFragment)
diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/menu/SettingsFragment.kt b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/menu/SettingsFragment.kt
new file mode 100644
index 0000000..87e822c
--- /dev/null
+++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/menu/SettingsFragment.kt
@@ -0,0 +1,141 @@
+package com.tarkvaraprojekt.mobileauthapp.menu
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.navigation.fragment.findNavController
+import com.tarkvaraprojekt.mobileauthapp.R
+import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentSettingsBinding
+import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
+
+/**
+ * This fragment allows the user to save the CAN and the PIN 1 and also to delete them if necessary.
+ * Should only be accessible for the user from the HomeFragment and not during the
+ * authentication process itself.
+ */
+class SettingsFragment : Fragment() {
+
+ private val viewModel: SmartCardViewModel by activityViewModels()
+
+ private var binding: FragmentSettingsBinding? = null
+
+ private var showPin: Boolean = false
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = FragmentSettingsBinding.inflate(inflater, container, false)
+ return binding!!.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ showCanField()
+ showPinField()
+ togglePinButton()
+ binding!!.canMenuAction.setOnClickListener { canAction() }
+ binding!!.pinMenuAction.setOnClickListener { pinAction() }
+ binding!!.pinMenuShow.setOnClickListener { togglePin() }
+ binding!!.returnButton.setOnClickListener { backToHome() }
+ }
+
+ /**
+ * Method for showing the CAN field to the user and can be used to refresh the field as well.
+ */
+ private fun showCanField() {
+ if (viewModel.userCan.length == 6) {
+ binding!!.canSaved.text = getString(R.string.saved_can, viewModel.userCan)
+ binding!!.canMenuAction.text = getString(R.string.can_delete)
+ } else {
+ binding!!.canSaved.text = getString(R.string.saved_can, getString(R.string.missing))
+ binding!!.canMenuAction.text = getString(R.string.can_add)
+ }
+ }
+
+ /**
+ * Method that allows the user to delete saved CAN from the device and also to save new a CAN if
+ * currently there is no CAN saved.
+ */
+ private fun canAction() {
+ if (viewModel.userCan.length == 6) {
+ viewModel.deleteCan(requireContext())
+ showCanField()
+ } else {
+ val action = SettingsFragmentDirections.actionSettingsFragmentToCanFragment(saving = true)
+ findNavController().navigate(action)
+ }
+ }
+
+ /**
+ * Method for showing the PIN 1 field to the user and can be used to refresh the field as well.
+ * The PIN 1 is hidden by default and when it is hidden it is always shown as **** despite the
+ * length of the PIN 1. Can be made visible with toggle button.
+ */
+ private fun showPinField() {
+ if (viewModel.userPin.length in 4..12) {
+ binding!!.pinMenuShow.visibility = Button.VISIBLE
+ if (showPin)
+ binding!!.pinSaved.text = getString(R.string.saved_pin, viewModel.userPin)
+ else
+ binding!!.pinSaved.text = getString(R.string.saved_pin, getString(R.string.hidden_pin))
+ binding!!.pinMenuAction.text = getString(R.string.pin1_delete)
+ } else {
+ binding!!.pinMenuShow.visibility = Button.GONE
+ binding!!.pinSaved.text = getString(R.string.saved_pin, getString(R.string.missing))
+ binding!!.pinMenuAction.text = getString(R.string.pin1_add)
+ }
+ }
+
+ /**
+ * Method that allows the user to delete saved PIN 1 from the device and also to save a new PIN 1 if
+ * currently there is no PIN 1 saved.
+ */
+ private fun pinAction() {
+ if (viewModel.userPin.length in 4..12) {
+ viewModel.deletePin(requireContext())
+ showPinField()
+ } else {
+ val action = SettingsFragmentDirections.actionSettingsFragmentToPinFragment(saving = true)
+ findNavController().navigate(action)
+ }
+ }
+
+ /**
+ * Hides the PIN 1 or makes it visible.
+ */
+ private fun togglePin() {
+ showPin = !showPin
+ togglePinButton()
+ showPinField()
+ }
+
+ /**
+ * Updates the text on the button that controls the visiblity of the PIN 1.
+ */
+ private fun togglePinButton() {
+ if (showPin) {
+ binding!!.pinMenuShow.text = getString(R.string.hide)
+ } else {
+ binding!!.pinMenuShow.text = getString(R.string.show)
+ }
+ }
+
+ /**
+ * Navigates back to home fragment.
+ */
+ private fun backToHome() {
+ findNavController().navigate(R.id.action_settingsFragment_to_homeFragment)
+ }
+
+ 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 4674831..1b66115 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
@@ -1,6 +1,10 @@
package com.tarkvaraprojekt.mobileauthapp.model
+import android.content.Context
+import android.content.SharedPreferences
import androidx.lifecycle.ViewModel
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.MasterKeys
class SmartCardViewModel: ViewModel() {
@@ -19,12 +23,24 @@ class SmartCardViewModel: ViewModel() {
private var _userIdentificationNumber: String = ""
val userIdentificationNumber get() = _userIdentificationNumber
+ private var _gender: String = ""
+ val gender get() = _gender
+
+ private var _expiration: String = ""
+ val expiration get() = _expiration
+
+ private var _citizenship: String = ""
+ val citizenship get() = _citizenship
+
fun clearUserInfo() {
_userPin = ""
_userCan = ""
_userFirstName = ""
_userLastName = ""
_userIdentificationNumber = ""
+ _expiration = ""
+ _citizenship = ""
+ _gender = ""
}
fun setUserPin(newUserPin: String) {
@@ -47,4 +63,67 @@ class SmartCardViewModel: ViewModel() {
_userIdentificationNumber = newUserIdentificationNumber
}
+ fun setExpiration(newExpiration: String) {
+ _expiration = newExpiration
+ }
+
+ fun setCitizenship(newCitizenship: String) {
+ _citizenship = newCitizenship
+ }
+
+ fun setGender(newGender: String) {
+ _gender = newGender
+ }
+
+
+ private fun getSharedPreferences(context: Context): SharedPreferences {
+ val masterKeyAlias: String = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
+ return EncryptedSharedPreferences.create(
+ "user_creds",
+ masterKeyAlias,
+ context,
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+ )
+ }
+
+ fun storeCan(context: Context) {
+ val sharedPreferences: SharedPreferences = getSharedPreferences(context)
+ sharedPreferences.edit().putString("CAN", userCan).apply()
+ }
+
+ fun checkCan(context: Context) {
+ val sharedPreferences: SharedPreferences = getSharedPreferences(context)
+ val foundCan = sharedPreferences.getString("CAN", null)
+ foundCan?.let {
+ _userCan = it
+ }
+ }
+
+ // Must be called from AuthFragment as well, when CAN is wrong.
+ fun deleteCan(context: Context) {
+ val sharedPreferences: SharedPreferences = getSharedPreferences(context)
+ sharedPreferences.edit().remove("CAN").apply()
+ _userCan = ""
+ }
+
+ fun storePin(context: Context) {
+ val sharedPreferences: SharedPreferences = getSharedPreferences(context)
+ sharedPreferences.edit().putString("PIN1", userPin).apply()
+ }
+
+ fun checkPin(context: Context) {
+ val sharedPreferences: SharedPreferences = getSharedPreferences(context)
+ val foundPin = sharedPreferences.getString("PIN1", null)
+ foundPin?.let {
+ _userPin = it
+ }
+ }
+
+ fun deletePin(context: Context) {
+ val sharedPreferences: SharedPreferences = getSharedPreferences(context)
+ sharedPreferences.edit().remove("PIN1").apply()
+ _userPin = ""
+ }
+
}
\ No newline at end of file
diff --git a/MobileAuthApp/app/src/main/res/color/stroke_color.xml b/MobileAuthApp/app/src/main/res/color/stroke_color.xml
new file mode 100644
index 0000000..52c9911
--- /dev/null
+++ b/MobileAuthApp/app/src/main/res/color/stroke_color.xml
@@ -0,0 +1,6 @@
+
+
+