content: package com.alimert.passportreader.ui;
  
  import android.Manifest;
  import android.app.Activity;
  import android.app.PendingIntent;
  import android.content.Intent;
  import android.content.pm.PackageManager;
  import android.graphics.Bitmap;
  import android.net.Uri;
  import android.nfc.NfcAdapter;
  import android.nfc.Tag;
  import android.nfc.tech.IsoDep;
  import android.os.AsyncTask;
  import android.os.Bundle;
  import android.provider.Settings;
  import android.util.Log;
  import android.view.View;
  import android.widget.Button;
  import android.widget.ImageView;
  import android.widget.TextView;
  
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.appcompat.app.ActionBar;
  import androidx.appcompat.app.AppCompatActivity;
  import androidx.core.app.ActivityCompat;
  
  import com.alimert.passportreader.R;
  import com.alimert.passportreader.model.AdditionalPersonDetails;
  import com.alimert.passportreader.model.DocType;
  import com.alimert.passportreader.model.EDocument;
  import com.alimert.passportreader.model.PersonDetails;
  import com.alimert.passportreader.util.AppUtil;
  import com.alimert.passportreader.util.DateUtil;
  import com.alimert.passportreader.util.Image;
  import com.alimert.passportreader.util.ImageUtil;
  import com.alimert.passportreader.util.PermissionUtil;
  import com.google.android.material.snackbar.Snackbar;
  
  import net.sf.scuba.smartcards.CardFileInputStream;
  import net.sf.scuba.smartcards.CardService;
  
  import org.jmrtd.BACKey;
  import org.jmrtd.BACKeySpec;
  import org.jmrtd.PassportService;
  import org.jmrtd.lds.CardSecurityFile;
  import org.jmrtd.lds.DisplayedImageInfo;
  import org.jmrtd.lds.PACEInfo;
  import org.jmrtd.lds.SecurityInfo;
  import org.jmrtd.lds.icao.DG11File;
  import org.jmrtd.lds.icao.DG15File;
  import org.jmrtd.lds.icao.DG1File;
  import org.jmrtd.lds.icao.DG2File;
  import org.jmrtd.lds.icao.DG3File;
  import org.jmrtd.lds.icao.DG5File;
  import org.jmrtd.lds.icao.DG7File;
  import org.jmrtd.lds.icao.MRZInfo;
  import org.jmrtd.lds.iso19794.FaceImageInfo;
  import org.jmrtd.lds.iso19794.FaceInfo;
  import org.jmrtd.lds.iso19794.FingerImageInfo;
  import org.jmrtd.lds.iso19794.FingerInfo;
  
  import java.security.PublicKey;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.List;
  
  import static com.alimert.passportreader.ui.CaptureActivity.DOC_TYPE;
  import static com.alimert.passportreader.ui.CaptureActivity.MRZ_RESULT;
  import static org.jmrtd.PassportService.DEFAULT_MAX_BLOCKSIZE;
  import static org.jmrtd.PassportService.NORMAL_MAX_TRANCEIVE_LENGTH;
  
  public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  
      private static final String TAG = MainActivity.class.getSimpleName();
  
      private static final int APP_CAMERA_ACTIVITY_REQUEST_CODE = 150;
      private static final int APP_SETTINGS_ACTIVITY_REQUEST_CODE = 550;
  
      private NfcAdapter adapter;
  
      private View mainLayout;
      private View loadingLayout;
      private View imageLayout;
      private Button scanIdCard, scanPassport, read;
      private TextView tvResult;
      private ImageView ivPhoto;
  
      private String passportNumber, expirationDate , birthDate;
      private DocType docType;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
  
          getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
          getSupportActionBar().setCustomView(R.layout.action_bar_title);
  
          mainLayout = findViewById(R.id.main_layout);
          loadingLayout = findViewById(R.id.loading_layout);
          imageLayout = findViewById(R.id.image_layout);
          ivPhoto = findViewById(R.id.view_photo);
          tvResult = findViewById(R.id.text_result);
          scanIdCard = findViewById(R.id.btn_scan_id_card);
          scanIdCard.setOnClickListener(this);
          scanPassport = findViewById(R.id.btn_scan_passport);
          scanPassport.setOnClickListener(this);
          read = findViewById(R.id.btn_read);
          read.setOnClickListener(this);
  
      }
  
      private void setMrzData(MRZInfo mrzInfo) {
          adapter = NfcAdapter.getDefaultAdapter(this);
          mainLayout.setVisibility(View.GONE);
          imageLayout.setVisibility(View.VISIBLE);
  
          passportNumber = mrzInfo.getDocumentNumber();
          expirationDate = mrzInfo.getDateOfExpiry();
          birthDate = mrzInfo.getDateOfBirth();
      }
  
      private void readCard() {
  
          String mrzData = "P<GBPANGELA<ZOE<<SMITH<<<<<<<<<<<<<<<<<<<<<<" +
                          "9990727768GBR7308196F2807041<<<<<<<<<<<<<<02";
  
          MRZInfo mrzInfo = new MRZInfo(mrzData);
          setMrzData(mrzInfo);
      }
  
      private void openCameraActivity() {
          Intent intent = new Intent(this, CaptureActivity.class);
          intent.putExtra(DOC_TYPE, docType);
          startActivityForResult(intent, APP_CAMERA_ACTIVITY_REQUEST_CODE);
      }
  
      @Override
      protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
          super.onActivityResult(requestCode, resultCode, data);
          if (resultCode == Activity.RESULT_OK) {
              switch (requestCode) {
                  case APP_CAMERA_ACTIVITY_REQUEST_CODE:
                      MRZInfo mrzInfo = (MRZInfo) data.getSerializableExtra(MRZ_RESULT);
                      if(mrzInfo != null) {
                          setMrzData(mrzInfo);
                      } else {
                          Snackbar.make(loadingLayout, R.string.error_input, Snackbar.LENGTH_SHORT).show();
                      }
                      break;
                  default:
                      break;
              }
          }
      }
  
      @Override
      public void onClick(View view) {
          switch (view.getId()) {
              case R.id.btn_scan_id_card:
                  docType = DocType.ID_CARD;
                  requestPermissionForCamera();
                  break;
              case R.id.btn_scan_passport:
                  docType = DocType.PASSPORT;
                  requestPermissionForCamera();
                  break;
              case R.id.btn_read:
                  readCard();
                  break;
              default:
                  break;
          }
      }
  
      @Override
      protected void onResume() {
          super.onResume();
  
          if (adapter != null) {
              Intent intent = new Intent(getApplicationContext(), this.getClass());
              intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
              PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
              String[][] filter = new String[][]{new String[]{"android.nfc.tech.IsoDep"}};
              adapter.enableForegroundDispatch(this, pendingIntent, null, filter);
          }
  
      }
  
      @Override
      protected void onPause() {
          super.onPause();
  
          if (adapter != null) {
              adapter.disableForegroundDispatch(this);
          }
      }
  
      @Override
      public void onNewIntent(Intent intent) {
          super.onNewIntent(intent);
          if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
              Tag tag = intent.getExtras().getParcelable(NfcAdapter.EXTRA_TAG);
              if (Arrays.asList(tag.getTechList()).contains("android.nfc.tech.IsoDep")) {
                  clearViews();
                  if (passportNumber != null && !passportNumber.isEmpty()
                          && expirationDate != null && !expirationDate.isEmpty()
                          && birthDate != null && !birthDate.isEmpty()) {
                      BACKeySpec bacKey = new BACKey(passportNumber, birthDate, expirationDate);
                      new ReadTask(IsoDep.get(tag), bacKey).execute();
                      mainLayout.setVisibility(View.GONE);
                      imageLayout.setVisibility(View.GONE);
                      loadingLayout.setVisibility(View.VISIBLE);
                  } else {
                      Snackbar.make(loadingLayout, R.string.error_input, Snackbar.LENGTH_SHORT).show();
                  }
              }
          }
      }
  
      private class ReadTask extends AsyncTask<Void, Void, Exception> {
  
          private IsoDep isoDep;
          private BACKeySpec bacKey;
  
          private ReadTask(IsoDep isoDep, BACKeySpec bacKey) {
              this.isoDep = isoDep;
              this.bacKey = bacKey;
          }
  
          EDocument eDocument = new EDocument();
          DocType docType = DocType.OTHER;
          PersonDetails personDetails = new PersonDetails();
          AdditionalPersonDetails additionalPersonDetails = new AdditionalPersonDetails();
  
          @Override
          protected Exception doInBackground(Void... params) {
  
              try {
                  CardService cardService = CardService.getInstance(isoDep);
                  cardService.open();
  
                  PassportService service = new PassportService(cardService, NORMAL_MAX_TRANCEIVE_LENGTH, DEFAULT_MAX_BLOCKSIZE, true, false);
                  service.open();
  
                  boolean paceSucceeded = false;
                  try {
                      CardSecurityFile cardSecurityFile = new CardSecurityFile(service.getInputStream(PassportService.EF_CARD_SECURITY));
                      Collection<SecurityInfo> securityInfoCollection = cardSecurityFile.getSecurityInfos();
                      for (SecurityInfo securityInfo : securityInfoCollection) {
                          if (securityInfo instanceof PACEInfo) {
                              PACEInfo paceInfo = (PACEInfo) securityInfo;
                              service.doPACE(bacKey, paceInfo.getObjectIdentifier(), PACEInfo.toParameterSpec(paceInfo.getParameterId()), null);
                              paceSucceeded = true;
                          }
                      }
                  } catch (Exception e) {
                      Log.w(TAG, e);
                  }
  
                  service.sendSelectApplet(paceSucceeded);
  
                  if (!paceSucceeded) {
                      try {
                          service.getInputStream(PassportService.EF_COM).read();
                      } catch (Exception e) {
                          service.doBAC(bacKey);
                      }
                  }
  
                  // -- Personal Details -- //
                  CardFileInputStream dg1In = service.getInputStream(PassportService.EF_DG1);
                  DG1File dg1File = new DG1File(dg1In);
  
                  MRZInfo mrzInfo = dg1File.getMRZInfo();
                  personDetails.setName(mrzInfo.getSecondaryIdentifier().replace("<", " ").trim());
                  personDetails.setSurname(mrzInfo.getPrimaryIdentifier().replace("<", " ").trim());
                  personDetails.setPersonalNumber(mrzInfo.getPersonalNumber());
                  personDetails.setGender(mrzInfo.getGender().toString());
                  personDetails.setBirthDate(DateUtil.convertFromMrzDate(mrzInfo.getDateOfBirth()));
                  personDetails.setExpiryDate(DateUtil.convertFromMrzDate(mrzInfo.getDateOfExpiry()));
                  personDetails.setSerialNumber(mrzInfo.getDocumentNumber());
                  personDetails.setNationality(mrzInfo.getNationality());
                  personDetails.setIssuerAuthority(mrzInfo.getIssuingState());
  
                  if("I".equals(mrzInfo.getDocumentCode())) {
                      docType = DocType.ID_CARD;
                  } else if("P".equals(mrzInfo.getDocumentCode())) {
                      docType = DocType.PASSPORT;
                  }
  
                  // -- Face Image -- //
                  CardFileInputStream dg2In = service.getInputStream(PassportService.EF_DG2);
                  DG2File dg2File = new DG2File(dg2In);
  
                  List<FaceInfo> faceInfos = dg2File.getFaceInfos();
                  List<FaceImageInfo> allFaceImageInfos = new ArrayList<>();
                  for (FaceInfo faceInfo : faceInfos) {
                      allFaceImageInfos.addAll(faceInfo.getFaceImageInfos());
                  }
  
                  if (!allFaceImageInfos.isEmpty()) {
                      FaceImageInfo faceImageInfo = allFaceImageInfos.iterator().next();
                      Image image = ImageUtil.getImage(MainActivity.this, faceImageInfo);
                      personDetails.setFaceImage(image.getBitmapImage());
                      personDetails.setFaceImageBase64(image.getBase64Image());
                  }
  
                  // -- Fingerprint (if exist)-- //
                  try {
                      CardFileInputStream dg3In = service.getInputStream(PassportService.EF_DG3);
                      DG3File dg3File = new DG3File(dg3In);
  
                      List<FingerInfo> fingerInfos = dg3File.getFingerInfos();
                      List<FingerImageInfo> allFingerImageInfos = new ArrayList<>();
                      for (FingerInfo fingerInfo : fingerInfos) {
                          allFingerImageInfos.addAll(fingerInfo.getFingerImageInfos());
                      }
  
                      List<Bitmap> fingerprintsImage = new ArrayList<>();
  
                      if (!allFingerImageInfos.isEmpty()) {
  
                          for(FingerImageInfo fingerImageInfo : allFingerImageInfos) {
                              Image image = ImageUtil.getImage(MainActivity.this, fingerImageInfo);
                              fingerprintsImage.add(image.getBitmapImage());
                          }
  
                          personDetails.setFingerprints(fingerprintsImage);
  
                      }
                  } catch (Exception e) {
                      Log.w(TAG, e);
                  }
  
                  // -- Portrait Picture -- //
                  try {
                      CardFileInputStream dg5In = service.getInputStream(PassportService.EF_DG5);
                      DG5File dg5File = new DG5File(dg5In);
  
                      List<DisplayedImageInfo> displayedImageInfos = dg5File.getImages();
                      if (!displayedImageInfos.isEmpty()) {
                          DisplayedImageInfo displayedImageInfo = displayedImageInfos.iterator().next();
                          Image image = ImageUtil.getImage(MainActivity.this, displayedImageInfo);
                          personDetails.setPortraitImage(image.getBitmapImage());
                          personDetails.setPortraitImageBase64(image.getBase64Image());
                      }
                  } catch (Exception e) {
                      Log.w(TAG, e);
                  }
  
                  // -- Signature (if exist) -- //
                  try {
                      CardFileInputStream dg7In = service.getInputStream(PassportService.EF_DG7);
                      DG7File dg7File = new DG7File(dg7In);
  
                      List<DisplayedImageInfo> signatureImageInfos = dg7File.getImages();
                      if (!signatureImageInfos.isEmpty()) {
                          DisplayedImageInfo displayedImageInfo = signatureImageInfos.iterator().next();
                          Image image = ImageUtil.getImage(MainActivity.this, displayedImageInfo);
                          personDetails.setPortraitImage(image.getBitmapImage());
                          personDetails.setPortraitImageBase64(image.getBase64Image());
                      }
  
                  } catch (Exception e) {
                      Log.w(TAG, e);
                  }
  
                  // -- Additional Details (if exist) -- //
                  try {
                      CardFileInputStream dg11In = service.getInputStream(PassportService.EF_DG11);
                      DG11File dg11File = new DG11File(dg11In);
  
                      if(dg11File.getLength() > 0) {
                          additionalPersonDetails.setCustodyInformation(dg11File.getCustodyInformation());
                          additionalPersonDetails.setNameOfHolder(dg11File.getNameOfHolder());
                          additionalPersonDetails.setFullDateOfBirth(dg11File.getFullDateOfBirth());
                          additionalPersonDetails.setOtherNames(dg11File.getOtherNames());
                          additionalPersonDetails.setOtherValidTDNumbers(dg11File.getOtherValidTDNumbers());
                          additionalPersonDetails.setPermanentAddress(dg11File.getPermanentAddress());
                          additionalPersonDetails.setPersonalNumber(dg11File.getPersonalNumber());
                          additionalPersonDetails.setPersonalSummary(dg11File.getPersonalSummary());
                          additionalPersonDetails.setPlaceOfBirth(dg11File.getPlaceOfBirth());
                          additionalPersonDetails.setProfession(dg11File.getProfession());
                          additionalPersonDetails.setProofOfCitizenship(dg11File.getProofOfCitizenship());
                          additionalPersonDetails.setTag(dg11File.getTag());
                          additionalPersonDetails.setTagPresenceList(dg11File.getTagPresenceList());
                          additionalPersonDetails.setTelephone(dg11File.getTelephone());
                          additionalPersonDetails.setTitle(dg11File.getTitle());
                      }
                  } catch (Exception e) {
                      Log.w(TAG, e);
                  }
  
                  // -- Document Public Key -- //
                  try {
                      CardFileInputStream dg15In = service.getInputStream(PassportService.EF_DG15);
                      DG15File dg15File = new DG15File(dg15In);
                      PublicKey publicKey = dg15File.getPublicKey();
                      eDocument.setDocPublicKey(publicKey);
                  } catch (Exception e) {
                      Log.w(TAG, e);
                  }
  
                  eDocument.setDocType(docType);
                  eDocument.setPersonDetails(personDetails);
                  eDocument.setAdditionalPersonDetails(additionalPersonDetails);
  
              } catch (Exception e) {
                  return e;
              }
              return null;
          }
  
          @Override
          protected void onPostExecute(Exception exception) {
              mainLayout.setVisibility(View.VISIBLE);
              loadingLayout.setVisibility(View.GONE);
  
              if (exception == null) {
                  setResultToView(eDocument);
              } else {
                  Snackbar.make(mainLayout, exception.getLocalizedMessage(), Snackbar.LENGTH_LONG).show();
              }
          }
  
      }
  
      private void setResultToView(EDocument eDocument) {
  
          Bitmap image = ImageUtil.scaleImage(eDocument.getPersonDetails().getFaceImage());
  
          ivPhoto.setImageBitmap(image);
  
          String result  = "NAME: " + eDocument.getPersonDetails().getName() + "\n";
          result += "SURNAME: " + eDocument.getPersonDetails().getSurname() + "\n";
          result += "PERSONAL NUMBER: " + eDocument.getPersonDetails().getPersonalNumber() + "\n";
          result += "GENDER: " + eDocument.getPersonDetails().getGender() + "\n";
          result += "BIRTH DATE: " + eDocument.getPersonDetails().getBirthDate() + "\n";
          result += "EXPIRY DATE: " + eDocument.getPersonDetails().getExpiryDate() + "\n";
          result += "SERIAL NUMBER: " + eDocument.getPersonDetails().getSerialNumber() + "\n";
          result += "NATIONALITY: " + eDocument.getPersonDetails().getNationality() + "\n";
          result += "DOC TYPE: " + eDocument.getDocType().name() + "\n";
          result += "ISSUER AUTHORITY: " + eDocument.getPersonDetails().getIssuerAuthority() + "\n";
  
          tvResult.setText(result);
      }
  
      private void clearViews() {
          ivPhoto.setImageBitmap(null);
          tvResult.setText("");
      }
  
      private void requestPermissionForCamera() {
          String[] permissions = { Manifest.permission.CAMERA };
          boolean isPermissionGranted = PermissionUtil.hasPermissions(this, permissions);
  
          if (!isPermissionGranted) {
              AppUtil.showAlertDialog(this, getString(R.string.permission_title), getString(R.string.permission_description), getString(R.string.button_ok), false, (dialogInterface, i) -> ActivityCompat.requestPermissions(this, permissions, PermissionUtil.REQUEST_CODE_MULTIPLE_PERMISSIONS));
          } else {
              openCameraActivity();
          }
      }
  
      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  
          if (requestCode == PermissionUtil.REQUEST_CODE_MULTIPLE_PERMISSIONS) {
              int result = grantResults[0];
              if (result == PackageManager.PERMISSION_DENIED) {
                  if (!PermissionUtil.showRationale(this, permissions[0])) {
                      Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                      Uri uri = Uri.fromParts("package", getPackageName(), null);
                      intent.setData(uri);
                      startActivityForResult(intent, APP_SETTINGS_ACTIVITY_REQUEST_CODE);
                  } else {
                      requestPermissionForCamera();
                  }
              } else if (result == PackageManager.PERMISSION_GRANTED) {
                  openCameraActivity();
              }
          }
      }
  
  }
  