package com.tarkvaraprojekt.mobileauthapp import android.app.Activity import android.content.Context import android.content.Intent import android.nfc.NfcAdapter import android.nfc.TagLostException import android.nfc.tech.IsoDep import android.os.Bundle import android.os.CountDownTimer 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.ParametersViewModel import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel import java.lang.Exception import kotlin.system.exitProcess /** * Fragment that asks the user to detect the ID card with mobile NFC chip. * Currently contains a next button that won't be needed later on. * This button is just needed to test navigation between fragments so that every step exists. */ class AuthFragment : Fragment() { private val viewModel: SmartCardViewModel by activityViewModels() private val paramsModel: ParametersViewModel by activityViewModels() private var _binding: FragmentAuthBinding? = null private val binding get() = _binding!! private val args: CanFragmentArgs by navArgs() private lateinit var timer: CountDownTimer private var timeRemaining: Int = 90 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentAuthBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) timer = object : CountDownTimer((timeRemaining * 1000).toLong(), 1000) { override fun onTick(p0: Long) { timeRemaining-- if (timeRemaining == 0) { binding.timeCounter.text = getString(R.string.no_time) } else { binding.timeCounter.text = getString(R.string.time_left, timeRemaining) } } override fun onFinish() { Thread.sleep(750) cancelAuth(408) } }.start() // The button exists in code for testing reasons, but not visible to the user anymore unless visibility is changed in the code. binding.nextButton.visibility = View.GONE binding.nextButton.setOnClickListener { goToNextFragment() } binding.cancelButton.setOnClickListener { cancelAuth(444) } val adapter = NfcAdapter.getDefaultAdapter(activity) if (adapter != null) getInfoFromIdCard(adapter) else { // If NFC adapter can not be detected then end the auth process as it is not possible to read an ID card cancelAuth(447) // It would be a good idea to show user some notification as it might be confusing if the app suddenly closes } } private fun goToNextFragment() { timer.cancel() val action = AuthFragmentDirections.actionAuthFragmentToResultFragment(mobile = args.mobile) findNavController().navigate(action) } private fun cancelAuth(code: Int) { viewModel.clearUserInfo() timer.cancel() if (args.mobile) { val resultIntent = Intent() requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent) requireActivity().finish() } else { (activity as MainActivity).returnError(code) requireActivity().finishAndRemoveTask() } } 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 jws = Authenticator(comms).authenticate( paramsModel.challenge, paramsModel.origin, viewModel.userPin ) paramsModel.setToken(jws) requireActivity().runOnUiThread { goToNextFragment() } } catch (e: Exception) { when(e) { is TagLostException -> requireActivity().runOnUiThread { binding!!.timeCounter.text = getString(R.string.id_card_removed_early) cancelAuth(444) } else -> { when ("invalid pin") { in e.message.toString().lowercase() -> requireActivity().runOnUiThread { val messagePieces = e.message.toString().split(" ") binding.timeCounter.text = getString(R.string.wrong_pin, messagePieces[messagePieces.size - 1]) viewModel.deletePin(requireContext()) cancelAuth(449) } else -> requireActivity().runOnUiThread { binding.timeCounter.text = getString(R.string.wrong_can_text) viewModel.deleteCan(requireContext()) cancelAuth(449) } } } } // Give user some time to read the error message Thread.sleep(2000) } finally { adapter.disableReaderMode(activity) } } }, NfcAdapter.FLAG_READER_NFC_A, null) } override fun onDestroy() { super.onDestroy() _binding = null } }