Custom Electronic Signature Flow
Showcases how to use an ElectronicSignatureFragment to implement custom ink signature flow in custom activities using PdfFragment.
/* * Copyright © 2017-2026 PSPDFKit GmbH. All rights reserved. * * The PSPDFKit Sample applications are licensed with a modified BSD license. * Please see License for details. This notice may not be removed from this file. */
package com.pspdfkit.catalog.examples.java;
import static com.pspdfkit.catalog.tasks.ExtractAssetTask.extract;
import android.content.Context;import android.content.Intent;import android.graphics.Color;import android.graphics.PointF;import android.net.Uri;import android.os.Bundle;import android.view.MotionEvent;import android.widget.Button;import androidx.annotation.IntRange;import androidx.annotation.NonNull;import androidx.annotation.Nullable;import androidx.appcompat.app.AlertDialog;import androidx.appcompat.app.AppCompatActivity;import com.pspdfkit.annotations.Annotation;import com.pspdfkit.catalog.R;import com.pspdfkit.catalog.SdkExample;import com.pspdfkit.configuration.PdfConfiguration;import com.pspdfkit.configuration.activity.PdfActivityConfiguration;import com.pspdfkit.configuration.signatures.SignatureColorOptions;import com.pspdfkit.configuration.signatures.SignatureCreationMode;import com.pspdfkit.configuration.signatures.SignatureSavingStrategy;import com.pspdfkit.document.PdfDocument;import com.pspdfkit.forms.FormElement;import com.pspdfkit.forms.FormType;import com.pspdfkit.listeners.DocumentListener;import com.pspdfkit.signatures.Signature;import com.pspdfkit.signatures.listeners.OnSignaturePickedListener;import com.pspdfkit.signatures.storage.DatabaseSignatureStorage;import com.pspdfkit.ui.PdfFragment;import com.pspdfkit.ui.annotations.OnAnnotationEditingModeChangeListener;import com.pspdfkit.ui.signatures.ElectronicSignatureFragment;import com.pspdfkit.ui.signatures.ElectronicSignatureOptions;import com.pspdfkit.ui.special_mode.controller.AnnotationEditingController;import com.pspdfkit.ui.special_mode.manager.FormManager;import com.pspdfkit.ui.toolbar.AnnotationEditingToolbar;import com.pspdfkit.ui.toolbar.ToolbarCoordinatorLayout;import java.util.Arrays;
/** * Showcases how to use {@link ElectronicSignatureFragment} to implement custom ink signature flow. * * This example adds a button to enable and disable signature creation when interacting with a * signature form element. */public class CustomElectronicSignatureExample extends SdkExample { public CustomElectronicSignatureExample(@NonNull final Context context) { super( context, R.string.customElectronicSignatureExampleTitle, R.string.customElectronicSignatureExampleDescription); }
@Override public void launchExample( @NonNull final Context context, @NonNull final PdfActivityConfiguration.Builder configuration) { extract(WELCOME_DOC, getTitle(), context, documentFile -> { final Intent intent = new Intent(context, CustomElectronicSignatureActivity.class); intent.putExtra(CustomElectronicSignatureActivity.EXTRA_URI, Uri.fromFile(documentFile)); context.startActivity(intent); }); }
/** * Showcases how to use {@link ElectronicSignatureFragment} to implement custom ink signature flow * in custom activities using {@link PdfFragment}. * * Uses a button to enable and disable signature creation when interacting with a signature form * element. */ public static class CustomElectronicSignatureActivity extends AppCompatActivity implements OnAnnotationEditingModeChangeListener, OnSignaturePickedListener {
public static final String EXTRA_URI = "CustomElectronicSignatureActivity.DocumentUri";
private static final String STATE_TOUCHED_PAGE_INDEX = "STATE_TOUCHED_PAGE_INDEX"; private static final String STATE_TOUCHED_POINT = "STATE_TOUCHED_POINT";
private PdfFragment fragment; private ToolbarCoordinatorLayout toolbarCoordinatorLayout; private Button annotationCreationButton;
private AnnotationEditingToolbar annotationEditingToolbar;
private DocumentListener documentListener;
private boolean annotationSignatureCreationActive; private PointF touchedPoint; private int touchedPageIndex;
// Here we are also showing how to configure the customizable signature colors and available // signing tabs (`signatureCreationModes`). @NonNull private final ElectronicSignatureOptions signatureOptions = new ElectronicSignatureOptions( SignatureSavingStrategy.SAVE_IF_SELECTED, SignatureColorOptions.fromColorInt(Color.BLACK, Color.GRAY, Color.RED), Arrays.asList(SignatureCreationMode.DRAW, SignatureCreationMode.TYPE));
@Override protected void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_ink_signature); setSupportActionBar(null);
toolbarCoordinatorLayout = findViewById(R.id.toolbarCoordinatorLayout); annotationEditingToolbar = new AnnotationEditingToolbar(this);
// The actual document Uri is provided with the launching intent. You can simply change that // inside the CustomSearchUiExample class. // This is a check that the example is not accidentally launched without a document Uri. final Uri uri = getIntent().getParcelableExtra(EXTRA_URI); if (uri == null) { new AlertDialog.Builder(this) .setTitle("Could not start example.") .setMessage("No document Uri was provided with the launching intent.") .setNegativeButton("Leave example", (dialog, which) -> dialog.dismiss()) .setOnDismissListener(dialog -> finish()) .show();
return; }
fragment = (PdfFragment) getSupportFragmentManager().findFragmentById(R.id.fragmentContainer); if (fragment == null) { fragment = PdfFragment.newInstance(uri, new PdfConfiguration.Builder().build()); // Signature field is on page index 16. fragment.setPageIndex(16); getSupportFragmentManager() .beginTransaction() .add(R.id.fragmentContainer, fragment) .commit(); }
// In this example we show how to create ink signatures by tapping on the page. // For this to work we need to handle page clicks in document listener. documentListener = new DocumentListener() { @Override public boolean onPageClick( @NonNull PdfDocument document, @IntRange(from = 0) final int pageIndex, @Nullable MotionEvent event, @Nullable final PointF pagePosition, @Nullable Annotation clickedAnnotation) { if (annotationSignatureCreationActive && pagePosition != null) {
// Store touched page index and touch point. touchedPageIndex = pageIndex; touchedPoint = pagePosition;
// Show signature picker. This will add the signature picker // fragment (if not added yet). // Note that if you set SignatureSavingStrategy.SAVE_IF_SELECTED, you // must provide a SignatureStorage. ElectronicSignatureFragment.show( getSupportFragmentManager(), CustomElectronicSignatureActivity.this, signatureOptions, DatabaseSignatureStorage.withName( getBaseContext(), DatabaseSignatureStorage.SIGNATURE_DB_NAME)); return true; } return false; } }; fragment.addDocumentListener(documentListener);
// Register annotation editing mode change listener for showing editing toolbar when // entering annotation editing mode. fragment.addOnAnnotationEditingModeChangeListener(this);
// For the sake of this example we toggle ink signature creation via simple button. annotationCreationButton = findViewById(R.id.createInkSignature); annotationCreationButton.setOnClickListener( v -> setSignatureCreationModeActive(!annotationSignatureCreationActive)); updateButtonText();
// Restore touched page index and touch point after configuration change. if (savedInstanceState != null) { touchedPageIndex = savedInstanceState.getInt(STATE_TOUCHED_PAGE_INDEX); touchedPoint = savedInstanceState.getParcelable(STATE_TOUCHED_POINT); }
// Restore existing signature picker dialog fragment. ElectronicSignatureFragment.restore(getSupportFragmentManager(), this);
fragment.addOnFormElementClickedListener(new FormManager.OnFormElementClickedListener() { @Override public boolean isFormElementClickable(@NonNull FormElement formElement) { // Returning false for signature fields prevents showing signature dialog // when clicking on the signature field. return formElement.getType() != FormType.SIGNATURE; }
@Override public boolean onFormElementClicked(@NonNull FormElement formElement) { // This click event is not interesting to us. Return false to let Nutrient // handle this event. return false; } }); }
@Override protected void onDestroy() { super.onDestroy(); fragment.removeOnAnnotationEditingModeChangeListener(this); if (documentListener != null) { fragment.removeDocumentListener(documentListener); } }
@Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); // Save touched page index and touch point here. outState.putInt(STATE_TOUCHED_PAGE_INDEX, touchedPageIndex); outState.putParcelable(STATE_TOUCHED_POINT, touchedPoint); }
private void setSignatureCreationModeActive(boolean isActive) { if (annotationSignatureCreationActive == isActive) return; annotationSignatureCreationActive = isActive;
if (isActive) { // Leave editing mode when entering signature creation mode. fragment.exitCurrentlyActiveMode(); } else { // Finish signature picker fragment when leaving signature creation mode. ElectronicSignatureFragment.dismiss(getSupportFragmentManager()); }
updateButtonText(); }
@Override public void onSignaturePicked(@NonNull final Signature signature) { // Leave signature creation mode after signature has been picked. setSignatureCreationModeActive(false); if (fragment.getDocument() == null || touchedPoint == null) return;
// Create signature annotation from returned signature data. final Annotation signatureAnnotation = signature.toAnnotation(fragment.getDocument(), touchedPageIndex, touchedPoint); // Set annotation's creator as set in annotation preferences. if (fragment.getAnnotationPreferences().isAnnotationCreatorSet()) { signatureAnnotation.setCreator( fragment.getAnnotationPreferences().getAnnotationCreator()); } // Add annotation to page and select it immediately for editing. fragment.addAnnotationToPage(signatureAnnotation, true); }
@Override public void onDismiss() { // In this example we leave signature creation mode when dismissing signature picker. setSignatureCreationModeActive(false); }
/** * Called when annotation editing mode has been entered. * * @param controller Controller for managing annotation editing. */ @Override public void onEnterAnnotationEditingMode(@NonNull AnnotationEditingController controller) { annotationEditingToolbar.bindController(controller); toolbarCoordinatorLayout.displayContextualToolbar(annotationEditingToolbar, true); }
/** * Called then annotation editing mode changes, meaning another annotation is being selected for * editing. * * @param controller Controller for managing annotation editing. */ @Override public void onChangeAnnotationEditingMode(@NonNull AnnotationEditingController controller) { // Nothing to be done here, if toolbar is bound to the controller it will pick up the // changes. }
/** * Called when annotation editing mode has been exited. * * @param controller Controller for managing annotation editing. */ @Override public void onExitAnnotationEditingMode(@NonNull AnnotationEditingController controller) { toolbarCoordinatorLayout.removeContextualToolbar(true); annotationEditingToolbar.unbindController(); }
private void updateButtonText() { annotationCreationButton.setText( annotationSignatureCreationActive ? R.string.signature_creation_enabled : R.string.signature_creation_disabled); } }}This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.