Skip to main content
The Android SDK provides various customization options with programming code or XML files.

Getting started

Create IdenfyUISettings

Create an instance of the IdenfyUISettings class:
    val idenfyUISettingsV2 = IdenfyUISettingsV2.IdenfyUIBuilderV2()
    .build()

Update IdenfyUISettings

    val idenfySettingsV2 = IdenfySettingsV2.IdenfyBuilderV2()
    .withIdenfyUISettingsV2(idenfyUISettingsV2)
    ...
    build()
The SDK currently supports three ways of customization.

Customization options

Customization with IdenfyUISettingsV2

Camera OnBoarding view

    IdenfyUISettingsV2.IdenfyUIBuilderV2()
    /**
     * OnBoarding View acts as additional screen, which helps user to familiarize himself with current step
     * @param idenfyOnBoardingViewTypeEnum Defines onBoarding view type
     */
    .withConfirmationView(idenfyOnBoardingViewTypeEnum: IdenfyOnBoardingViewTypeEnum)
    ...
    build()
The possible options of the Camera OnBoarding View:
IdenfyOnBoardingViewTypeEnumDescriptionUI
NONEOnBoarding view is skipped
MULTIPLE_STATICShows an onBoarding view before EVERY step of the verification process with a static instruction listMultiple Static

Language selection

    IdenfyUISettingsV2.IdenfyUIBuilderV2()
    /**
     * Enables language selection window, which provides an option to change the locale
     * @param isLanguageSelectionNeeded Changes visibility of locale selection icon.
    */
    .withLanguageSelection(Boolean isLanguageSelectionNeeded)
    ...
    build()

Mismatch tags alert visibility

    IdenfyUISettingsV2.IdenfyUIBuilderV2()
    /**
     * An option to choose whether mismatch tags alert is visible
     * @param mismatchTagsAlert: set the visibility of mismatch tags alert
    */
    .withMismatchTagsAlert(Boolean mismatchTagsAlert)
    ...
    build()

Document camera rectangle visibility

Since some documents are non-regular size, you can hide the camera rectangle. This way the whole screen is dedicated to document capturing. The rectangle can be hidden for all document types:
    /**
    * Camera rectangle will be hidden for ALL countries and document types
    */
    val idenfyUISettingsV2 =
        IdenfyUISettingsV2.IdenfyUIBuilderV2()
          .withDocumentFrameVisibility(DocumentCameraFrameVisibility.HiddenForAllCountriesAndDocumentTypes)
          .build()
Or for specific countries and document types:
    /**
    * Camera rectangle will be hidden ONLY for Lithuanian passport
    */
    val countryDocumentMap: MutableMap<String, List<DocumentTypeEnum>> = mutableMapOf()
    countryDocumentMap["LT"] = mutableListOf(DocumentTypeEnum.PASSPORT)
    val documentCameraFrameVisibility = DocumentCameraFrameVisibility.HiddenForSpecificCountriesAndDocumentTypes(countryDocumentMap)

    val idenfyUISettingsV2 = IdenfyUISettingsV2.IdenfyUIBuilderV2()
        .withDocumentFrameVisibility(documentCameraFrameVisibility)
        .build()

Adding instructions in camera session

The SDK provides informative instructions during the verification session. They can provide valuable information for the user and help tackle common issues: bad lighting, wrong document side, etc. Instructions can be customized by changing all UI elements or even using your MP4 video files. Instructions are configured by your backend settings and can be overridden with the SDK settings. Using IdenfyInstructionsEnum dialog: Instructions Dialog Using IdenfyInstructionsEnum none: Instructions None Enable instructions in IdenfyUISettingsV2:
    val idenfyUISettingsV2 = IdenfyUISettingsV2.IdenfyUIBuilderV2()
        .withInstructions(IdenfyInstructionsType.DIALOG)
        ...
        build()

Applying SDK-wide color changes

If color and asset changes are the only requirement, they can be easily customized by changing the main colors.
Color nameDescriptionDefault color value
idenfyMainColorV2Defines the color of most single-colored assets and focused parts in the SDK.#536DFE
idenfyMainDarkerColorV2Defines the color of some focused parts in the SDK, similar to idenfyMainColorV2.#5D7CE4
idenfyBackgroundColorV2Defines the background color.#FBFBFB
idenfySecondColorV2Defines the text color.#F2353B4E
1. Override color names in your app module. Create either a new idenfy_colors.xml or add the defined colors to your project. 2. Make color changes:
<resources>
    <color name="idenfyMainColorV2">#7CFC00</color>
    <color name="idenfyMainDarkerColorV2">#7CFC00</color>
</resources>
Colors are also applied to images that use a single color from idenfy drawable resources. If you override the provided images with icons using more than one color, you can disable the tint on images by overriding the layout styles with the removed tint attribute. Before:
<style name="idenfyAppBarLayoutBackButtonStyle">
    <item name="android:tint">
        @color/idenfyMainColorV2
    </item>
</style>
After:
<style name="idenfyAppBarLayoutBackButtonStyle" />

Customization with styles.xml or colors.xml

Every screen in the SDK uses different styles.xml which covers all UI elements visible on that screen. By overriding styles or colors in your app target, you can change the look of the SDK to match your brand guidelines. You can access the styles.xml and colors.xml here.

Customization with overriding layouts of SDK

All layouts in the SDK are structured so that it is easy to override all components to match your brand identity. Requirements for overriding layouts:
  • Do not remove IDs of components — this will maintain project stability and avoid runtime crashes.
  • Keep the same layout names — if layout names are changed, the SDK layouts will not be overridden.
All layouts can be found here.

Customization by providing your own implementations of Jetpack Compose Composables

For more advanced customization (fonts, layout structure, etc.), you can provide your own composable implementations.
This is a new feature that is still in progress. Jetpack Compose is NOT supported in all the views. We strongly suggest trying it out and sharing your feedback with us. More views will be supported in the future.
1. Create an instance of IdenfyComposableViews Use IdenfyComposeViewBuilder to create an instance of IdenfyComposableViews with your custom composables, which conform to an interface provided by the SDK:
  val idenfyComposeViews = IdenfyComposeViewBuilder()
    .withManualReviewingIdentificationResultsStatusWaitingComposable { data -> ManualReviewingIdentificationResultsWaitingTestComposable.composeManualView(data) }
    ...
    .build()
2. Pass IdenfyComposableViews instance to the SDK
IdenfyController.getInstance().idenfyComposableViews = idenfyComposeViews
Make sure you create an instance of IdenfyComposableViews in the Application class, otherwise your provided composables will be lost after process death.
3. Pass resources to composables IdenfyComposeViewBuilder provides a data class with all required resources (images, videos, button actions, etc.) to fulfil the view. Pass this class to your composable:
val idenfyComposeViews = IdenfyComposeViewBuilder()
    .withManualReviewingIdentificationResultsStatusWaitingComposable { data -> ManualReviewingIdentificationResultsWaitingTestComposable.composeManualView(data) }
    ...
    .build()

@Composable
fun composeManualView(data: IdenfyManualReviewingIdentificationResultsStatusWaitingComposeViewData) {
  ...
}
4. Create the composable The data class also contains IdenfyComposeBases, which you can use to provide ONLY your customized separate composables. These composables will be composed by the SDK, preserving the intended layout guidelines while displaying your own customized composables. A complete example:
    @Composable
fun composeManualView(data: IdenfyManualReviewingIdentificationResultsStatusWaitingComposeViewData) {
  //All needed resources for the view
  val resources = data.resources
  //A base for the composable
  val composeBases = data.idenfyComposeBases
  //States of the view
  val state = resources.state.collectAsState()

  composeBases.manualReviewingIdentificationResultsStatusWaitingComposableBase(
    resources,
    manualReviewingWaitingStatusViewTitle = { title ->
      Text(
        modifier = Modifier
          .padding(16.dp, 32.dp, 16.dp, 0.dp),
        text = title,
        textAlign = TextAlign.Center,
        color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonInformationTitleTextColor),
        fontSize = TextUnit(20f, TextUnitType.Sp),
        fontWeight = FontWeight.Bold,
        fontFamily = IdenfyFonts.hkGrotesk
      )
    },
    manualReviewingWaitingStatusViewDescription = { description ->
      Text(
        modifier = Modifier
          .padding(16.dp, 0.dp, 16.dp, 0.dp),
        text = description,
        textAlign = TextAlign.Center,
        color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonInformationDescriptionTextColor),
        fontSize = TextUnit(15f, TextUnitType.Sp),
        fontWeight = FontWeight.Normal,
        fontFamily = IdenfyFonts.hkGrotesk
      )
    },
    manualReviewingWaitingStatusViewAutomatedReviewBox = { firstCardTitle ->
      Surface(
        elevation = 4.dp,
        shape = RoundedCornerShape(4.dp),
        color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxFinishedBackgroundColor),
        modifier = Modifier
          .height(IntrinsicSize.Min)
          .padding(start = 16.dp, end = 16.dp)
          .fillMaxWidth()
      ) {
        Box {
          Row(
            horizontalArrangement = Arrangement.End,
            modifier = Modifier
              .requiredHeight(60.dp)
          ) {
            Text(
              modifier = Modifier
                .weight(weight = 1f)
                .padding(start = 16.dp, end = 16.dp)
                .align(Alignment.CenterVertically),
              textAlign = TextAlign.Start,
              text = firstCardTitle,
              color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxFinishedTitleColor),
              fontSize = TextUnit(13f, TextUnitType.Sp),
              fontWeight = FontWeight.SemiBold,
              fontFamily = IdenfyFonts.hkGrotesk
            )
            Image(
              painterResource(R.drawable.idenfy_ic_language_selection_language_selected_tick),
              contentDescription = "",
              colorFilter = ColorFilter.tint(colorResource(R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxFinishedTickImageTintColor)),
              modifier = Modifier
                .padding(end = 16.dp)
                .align(Alignment.CenterVertically)
                .wrapContentWidth(Alignment.End)
                .requiredSize(20.dp),
            )
          }
        }
      }
    },
    manualReviewingWaitingStatusViewManualReviewBox = { secondCardTitle ->
      Surface(
        elevation = 4.dp,
        shape = RoundedCornerShape(4.dp),
        color = if (state.value == ManualWaitingScreenState.WAITING) colorResource(
          R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxWaitingBackgroundColor
        ) else colorResource(R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxFinishedBackgroundColor),
        modifier = Modifier
          .height(IntrinsicSize.Min)
          .padding(start = 16.dp, end = 16.dp)
          .fillMaxWidth()
      ) {
        Box {
          Row(
            horizontalArrangement = Arrangement.End,
            modifier = Modifier
              .requiredHeight(60.dp)
          ) {
            Text(
              modifier = Modifier
                .weight(weight = 1f)
                .padding(start = 16.dp, end = 16.dp)
                .align(Alignment.CenterVertically),
              textAlign = TextAlign.Start,
              text = secondCardTitle,
              color = if (state.value == ManualWaitingScreenState.WAITING) colorResource(
                R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxWaitingTitleColor
              ) else colorResource(R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxFinishedTitleColor),
              fontSize = TextUnit(13f, TextUnitType.Sp),
              fontWeight = FontWeight.SemiBold,
              fontFamily = IdenfyFonts.hkGrotesk
            )
            if (state.value == ManualWaitingScreenState.WAITING) {
              CircularProgressIndicator(
                modifier = Modifier
                  .padding(end = 16.dp)
                  .align(Alignment.CenterVertically)
                  .wrapContentWidth(Alignment.End)
                  .requiredSize(20.dp),
                color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxWaitingSpinnerColor)
              )
            } else {
              Image(
                painterResource(R.drawable.idenfy_ic_language_selection_language_selected_tick),
                contentDescription = "",
                colorFilter = ColorFilter.tint(colorResource(R.color.idenfyManualReviewingStatusWaitingCommonReviewBoxFinishedTickImageTintColor)),
                modifier = Modifier
                  .padding(end = 16.dp)
                  .align(Alignment.CenterVertically)
                  .wrapContentWidth(Alignment.End)
                  .requiredSize(20.dp),
              )
            }
          }
        }
      }
    },
    manualReviewingWaitingStatusViewWaitingDurationTitle = { waitingDurationTitle ->
      Text(
        modifier = Modifier
          .padding(bottom = 8.dp)
          .wrapContentWidth(Alignment.Start),
        text = waitingDurationTitle,
        textAlign = TextAlign.Center,
        color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonWaitingDurationTitleColor),
        fontSize = TextUnit(13f, TextUnitType.Sp),
        fontWeight = FontWeight.Normal,
        fontFamily = IdenfyFonts.hkGrotesk
      )
    },
    manualReviewingWaitingStatusViewWaitingTimerBox = { timeState ->
      val time = timeState.collectAsState()
      Surface(
        shape = RoundedCornerShape(4.dp),
        color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonWaitingTimerBoxBackgroundColor),
        modifier = Modifier
          .height(42.dp)
          .padding(start = 16.dp, end = 16.dp)
          .fillMaxWidth()
      ) {
        Row(horizontalArrangement = Arrangement.Center) {
          Image(
            painterResource(R.drawable.idenfy_ic_waiting_results_timer_clock_v2),
            contentDescription = "",
            modifier = Modifier
              .size(25.dp)
              .align(Alignment.CenterVertically),
            colorFilter = ColorFilter.tint(colorResource(R.color.idenfyManualReviewingStatusWaitingCommonWaitingTimerImageTintColor))
          )
          Text(
            text = time.value,
            color = colorResource(R.color.idenfyManualReviewingStatusWaitingCommonWaitingTimerTitleColor),
            fontSize = TextUnit(12f, TextUnitType.Sp),
            fontWeight = FontWeight.Bold,
            fontFamily = IdenfyFonts.hkGrotesk,
            modifier = Modifier
              .align(Alignment.CenterVertically)
              .padding(start = 8.dp)
          )
        }
      }
    },
    manualReviewingWaitingStatusViewBackToAccountButton = { buttonResources ->
      when (buttonResources) {
        BackToAccountButtonResources.Hidden -> {
        }
        is BackToAccountButtonResources.Visible -> {
          Surface(
            shape = RoundedCornerShape(4.dp),
            color = colorResource(R.color.idenfyManualReviewingStatusWaitingBackToAccountButtonBackgroundColor),
            modifier = Modifier
              .height(42.dp)
              .padding(start = 16.dp, end = 16.dp)
              .clickable(onClick = buttonResources.buttonAction)
              .fillMaxWidth()
          ) {
            Row(horizontalArrangement = Arrangement.Center) {
              Text(
                text = buttonResources.title,
                color = colorResource(R.color.idenfyManualReviewingStatusWaitingBackToAccountButtonTextColor),
                fontSize = TextUnit(12f, TextUnitType.Sp),
                fontWeight = FontWeight.Bold,
                fontFamily = IdenfyFonts.hkGrotesk,
                modifier = Modifier
                  .align(Alignment.CenterVertically)
              )
            }
          }
        }
      }
    })
}
If you want to compose the view entirely by yourself, you are not required to use the IdenfyComposeBases class.
If you are composing the view yourself, make sure you carefully test the views, since the layout will NOT be used as intended and unexpected behavior might occur.

Example of the customization flow

Download the sample app. Check the IdenfyApplication class, where you will find IdenfyComposeViewBuilder with all the composables composed by IdenfyComposeBases.
We strongly suggest taking a look at the example class, since it shows how to collect the view state, pass the resources, and handle button actions.

Customization by providing a custom verification results view

To fully customize your verification results waiting view, you can pass your own fragment.
After supplying your own implementation of the results Fragment, the SDK will not load its own Fragment and will navigate directly to your own Fragment.
You can then control when and after which additional steps you want to retry the verification session: Custom Fragment 1. Create a class that implements IdenfyInProcessIdentificationResultsHandler Pass an instance of your created class to the setIdenfyCallBackHandlerAfterSDKCloses() method of IdenfyCallbackController:
  val idenfyCallBackHandlerAfterSDKCloses = IdenfyCallbackHandler()
        com.idenfy.idenfySdk.CoreSdkInitialization.IdenfyCallbackController.setIdenfyCallBackHandlerAfterSDKCloses(
            idenfyCallBackHandlerAfterSDKCloses
        )
2. Create an instance of your CustomWaitingViewController Return the created instance in the onIdenfyFlowFinished() method of your IdenfyInProcessIdentificationResultsDelegate implementation:
    /**
     - Parameter idenfyIdentificationResultStatus: returns a current verification status. This status is updated until FINISHED is returned.
     */
     override fun onIdenfyFlowFinished(idenfyFlowSettings: IdenfyFlowSettings): CustomWaitingFragment {
        return CustomWaitingFragment.FragmentProvided(PartnersCustomWaitingFragment())
    }
For details regarding the verification process, see IdenfyFlowSettings:
/**
 Has set of properties for providing information about user verification flow.
 Steps provides an array of steps used during the verification process.
 */
data class IdenfyFlowSettings(val steps: List<Step>)
3. Call a static method of IdenfyCallbackController to continue the flow Your IdenfyInProcessIdentificationResultsHandler implementation has an onIdentificationStatusReceived() method that returns an IdenfyIdentificationResultStatus. When IdenfyIdentificationStatus is FINISHED, call the static continueFlow() method of IdenfyCallbackController:
    /**
     - Parameter idenfyIdentificationResultStatus: returns a current verification status. This status is updated until FINISHED is returned.
     */
   override fun onIdentificationStatusReceived(idenfyIdentificationResultStatus: IdenfyIdentificationResultStatus) {
        when (val state = idenfyIdentificationResultStatus.idenfyProcessingResultState) {
            is IdenfyProcessingResultState.FINISHED -> {
                IdenfyCallbackController.continueFlow()
            }
            IdenfyProcessingResultState.PROCESSING -> {
            }
        }
    }
IdenfyIdentificationResultStatus contains all information about the current state of verification results, which you can use to fully customize your views:
data class IdenfyIdentificationResultStatus(val idenfyIdentificationStatus: IdenfyIdentificationStatus, val idenfyProcessingResultState: IdenfyProcessingResultState)

sealed class IdenfyProcessingResultState {
    data class FINISHED(val canRetry: Boolean, val retakeSteps: RetakeSteps?): IdenfyProcessingResultState()
    object PROCESSING: IdenfyProcessingResultState()
}

enum class IdenfyIdentificationStatus(val status: String) {
    SUSPECTED("SUSPECTED"), DENIED("DENIED"), APPROVED("APPROVED"), REVIEWING("REVIEWING"), UNVERIFIED("UNVERIFIED");

}

Liveness customization

The SDK provides additional liveness customization.

1. Creating IdenfyLivenessUIHelper

    val idenfyLivenessUISettings: IdenfyLivenessUISettings = IdenfyLivenessUISettings()

2.1 Applying regular settings

If you only need color, text, or width customization, you can use properties from the IdenfyLivenessUISettings class:
   class IdenfyLivenessUISettings() {

    //Liveness session feedback settings
    var livenessFeedbackBackgroundColor: Int? = null
    var livenessFeedbackFont: Typeface? = null

    //Liveness session frame settings
    var livenessFrameBackgroundColor: Int? = null
    var livenessFrameColor: Int? = null
    var livenessFrameWidth: Int? = null

    //Liveness session cancel button settings
    var livenessCancelButtonImage:Int?=null

    //Liveness session progress settings
    var livenessIdentificationOvalProgressColor1: Int? = null
    var livenessIdentificationOvalProgressColor2: Int? = null
    var livenessIdentificationProgressStrokeWidth: Int? = null
    var livenessIdentificationProgressRadialOffset: Int? = null
    var livenessIdentificationProgressStrokeColor: Int? = null

    //Liveness session overlay settings
    var livenessOverlayBrandingImage: Int? = null

    //Liveness ready screen settings
    var livenessReadyScreenForegroundColor: Int? = null
    var livenessReadyScreenBackgroundColor: Int? = null
    var livenessReadyScreenTextBackgroundColor: Int? = null
    var livenessReadyScreenButtonBorderColor: Int? = null
    var livenessReadyScreenButtonBorderWidth: Int? = null
    var livenessReadyScreenButtonCornerRadius: Int? = null
    var livenessReadyScreenButtonBackgroundNormalColor: Int? = null
    var livenessReadyScreenButtonBackgroundHighlightedColor: Int? = null
    var livenessReadyScreenButtonBackgroundDisabledColor: Int? = null
    var livenessReadyScreenShowBrandingImage: Boolean? = true

    //Camera Permission
    var livenessCameraPermissionsScreenImage:Int?=null

    //Liveness result screen settings
    var livenessResultScreenForegroundColor: Int? = null
    var livenessResultScreenIndicatorColor: Int? = null
    var livenessResultScreenUploadProgressFillColor: Int? = null
    var livenessResultScreenUploadProgressTrackColor: Int? = null
    var livenessResultScreenShowUploadProgressBar: Boolean? = true
    var livenessResultScreenResultAnimationSuccessBackgroundImage: Int? = null

    //Liveness id check customization
    var livenessIdCheckCustomization = LivenessIdCheckCustomization()

    //Full custom settings
    var livenessCustomUISettings: com.facetec.zoom.sdk.ZoomCustomization? = null
   }
LivenessIdCheckCustomization:
class LivenessIdCheckCustomization() {
        var buttonBackgroundNormalColor: Int?=null
        var buttonBackgroundHighlightColor: Int?=null
        var captureScreenTextBackgroundColor: Int?=null
        var reviewScreenTextBackgroundColor: Int?=null
        var captureFrameStrokeColor: Int?=null
    }

2.2 Applying full customization

If you require more changes, you can directly set the livenessCustomUISettings property in IdenfyLivenessUISettings with your instance of the FaceTecCustomization class:
    idenfyLivenessUISettings.livenessCustomUISettings = FaceTecCustomization()
Full customization options are available here.
This will override all other set properties of the IdenfyLivenessUISettings class.

3. Updating IdenfyUISettings

    val idenfyUISettingsV2 = IdenfyUISettingsV2.IdenfyUIBuilderV2()
        .withLivenessUISettings(idenfyLivenessUISettingsV2)
        ...
        build()