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 @@ + + + + + + \ No newline at end of file diff --git a/MobileAuthApp/app/src/main/res/drawable/ic_baseline_language_24.xml b/MobileAuthApp/app/src/main/res/drawable/ic_baseline_language_24.xml new file mode 100644 index 0000000..3f70646 --- /dev/null +++ b/MobileAuthApp/app/src/main/res/drawable/ic_baseline_language_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/MobileAuthApp/app/src/main/res/drawable/ic_check_logo.xml b/MobileAuthApp/app/src/main/res/drawable/ic_check_logo.xml new file mode 100644 index 0000000..5e111ca --- /dev/null +++ b/MobileAuthApp/app/src/main/res/drawable/ic_check_logo.xml @@ -0,0 +1,10 @@ + + + diff --git a/MobileAuthApp/app/src/main/res/drawable/ic_info_logo.xml b/MobileAuthApp/app/src/main/res/drawable/ic_info_logo.xml new file mode 100644 index 0000000..17255b7 --- /dev/null +++ b/MobileAuthApp/app/src/main/res/drawable/ic_info_logo.xml @@ -0,0 +1,10 @@ + + + diff --git a/MobileAuthApp/app/src/main/res/drawable/ic_settings.xml b/MobileAuthApp/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..41a82ed --- /dev/null +++ b/MobileAuthApp/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,10 @@ + + + diff --git a/MobileAuthApp/app/src/main/res/layout/activity_main.xml b/MobileAuthApp/app/src/main/res/layout/activity_main.xml index 39c9655..2ad4133 100644 --- a/MobileAuthApp/app/src/main/res/layout/activity_main.xml +++ b/MobileAuthApp/app/src/main/res/layout/activity_main.xml @@ -6,12 +6,28 @@ android:layout_height="match_parent" tools:context=".MainActivity"> + + + app:navGraph="@navigation/nav_graph" + app:layout_constraintTop_toBottomOf="@id/toolbar" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent"/> \ No newline at end of file diff --git a/MobileAuthApp/app/src/main/res/layout/fragment_auth.xml b/MobileAuthApp/app/src/main/res/layout/fragment_auth.xml index 3103c59..59652f3 100644 --- a/MobileAuthApp/app/src/main/res/layout/fragment_auth.xml +++ b/MobileAuthApp/app/src/main/res/layout/fragment_auth.xml @@ -14,7 +14,10 @@ android:layout_margin="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintTop_toTopOf="parent" + app:strokeWidth="1dp" + app:strokeColor="@color/stroke_color" + app:cardElevation="0dp"> @@ -70,7 +73,7 @@ android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="@string/cancel_text" - android:textSize="18sp" + android:textSize="15sp" app:layout_constraintEnd_toStartOf="@id/next_button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/card_view" /> diff --git a/MobileAuthApp/app/src/main/res/layout/fragment_can.xml b/MobileAuthApp/app/src/main/res/layout/fragment_can.xml index 2ee87b6..f7bcd1e 100644 --- a/MobileAuthApp/app/src/main/res/layout/fragment_can.xml +++ b/MobileAuthApp/app/src/main/res/layout/fragment_can.xml @@ -14,7 +14,10 @@ android:layout_margin="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintTop_toTopOf="parent" + app:strokeWidth="1dp" + app:strokeColor="@color/stroke_color" + app:cardElevation="0dp"> @@ -74,7 +77,7 @@ android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="@string/cancel_text" - android:textSize="18dp" + android:textSize="15sp" app:layout_constraintEnd_toStartOf="@id/next_button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/card_view" /> diff --git a/MobileAuthApp/app/src/main/res/layout/fragment_home.xml b/MobileAuthApp/app/src/main/res/layout/fragment_home.xml index dc30b64..dae476b 100644 --- a/MobileAuthApp/app/src/main/res/layout/fragment_home.xml +++ b/MobileAuthApp/app/src/main/res/layout/fragment_home.xml @@ -7,23 +7,87 @@ android:padding="24dp" tools:context=".HomeFragment"> - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + -