In this post, I will show you how to Create Digital Signature in Android. Project Structure: Create a new Project in Eclipse/Android ...

How to Create a Digital Signature Application in Android How to Create a Digital Signature Application in Android

How to Create a Digital Signature Application in Android

How to Create a Digital Signature Application in Android

How to Create a Digital Signature Application in Android
In this post, I will show you how to Create Digital Signature in Android.

Project Structure:

Create a new Project in Eclipse/Android Studio with the required Specifications.

Codes:

AndroidManifest.xml
Don't forget to add the following permission in your manifest file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
color.xml
Create color.xml and replace it with the following code
<resources>
    <color name="ColorPrimaryDark">#3367d6</color>
    <color name="ColorPrimary">#4285f4</color>
</resources>
strings.xml
Create strings.xml and replace it with the following code
<resources>
 <string name="app_name">Digital Signature</string>
 <string name="hello_world">Hello world!</string>
 <string name="action_settings">Settings</string>
 <string name="hint_sign">Get Signature</string>
 <string name="dialog_title">SIGN HERE</string>
 <string name="hint_cancel">Cancel</string>
 <string name="hint_clear">Clear</string>
 <string name="hint_save">Save</string>
</resources>
activity_toolbar.xml
Create activity_toolbar.xml and replace it with the following code
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="@string/app_name">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:theme="@style/AppTheme"
        app:popupTheme="@style/AppTheme" />

</RelativeLayout>
activity_main.xml
Create activity_main.xml and replace it with the following code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="@android:color/white">

    <include layout="@layout/activity_toolbar" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        android:gravity="center">

        <Button
            android:id="@+id/signature"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hint_sign"
            android:padding="5dp"
            android:background="@color/ColorPrimary" />
    </LinearLayout>

</LinearLayout>
dialog_signature.xml
Create dialog_signature.xml and replace it with the following code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearLayout1"
    android:layout_width="fill_parent"
    android:layout_height="400dp"
    android:orientation="vertical"
    android:background="@android:color/white">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/ColorPrimaryDark"
        android:text="@string/dialog_title"
        android:textColor="@android:color/white"
        android:padding="5dp"
        android:gravity="center"/>

    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:weightSum="3"
        android:background="@android:color/white">

        <Button
            android:id="@+id/cancel"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:text="@string/hint_cancel"
            tools:ignore="ButtonStyle"
            android:textColor="@android:color/white"
            android:background="@color/ColorPrimary" />

        <Button
            android:id="@+id/clear"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:text="@string/hint_clear"
            tools:ignore="ButtonStyle"
            android:textColor="@android:color/white"
            android:background="@color/ColorPrimary" />

        <Button
            android:id="@+id/getsign"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:text="@string/hint_save"
            tools:ignore="ButtonStyle"
            android:textColor="@android:color/white"
            android:background="@color/ColorPrimary" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#fff"
        android:orientation="vertical" />

</LinearLayout>
MainActivity.java
Open MainActivity.java and replace it with the following code
package com.example.digitalsignature;
 
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
 
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
 
public class MainActivity extends AppCompatActivity {

 Toolbar toolbar;
 Button btn_get_sign, mClear, mGetSign, mCancel;

 File file;
 Dialog dialog;
 LinearLayout mContent;
 View view;
 signature mSignature;
 Bitmap bitmap;

 // Creating Separate Directory for saving Generated Images
 String DIRECTORY = Environment.getExternalStorageDirectory().getPath() + "/DigitSign/";
 String pic_name = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
 String StoredPath = DIRECTORY + pic_name + ".png";

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // Setting ToolBar as ActionBar
  toolbar = (Toolbar) findViewById(R.id.toolbar);
  setSupportActionBar(toolbar);

  // Button to open signature panel
  btn_get_sign = (Button) findViewById(R.id.signature);

  // Method to create Directory, if the Directory doesn't exists
  file = new File(DIRECTORY);
  if (!file.exists()) {
   file.mkdir();
  }

  // Dialog Function
  dialog = new Dialog(MainActivity.this);
  // Removing the features of Normal Dialogs
  dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
  dialog.setContentView(R.layout.dialog_signature);
  dialog.setCancelable(true);

  btn_get_sign.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
   // Function call for Digital Signature
    dialog_action();

   }
  });
 }
 // Function for Digital Signature
 public void dialog_action() {
 
  mContent = (LinearLayout) dialog.findViewById(R.id.linearLayout);
  mSignature = new signature(getApplicationContext(), null);
  mSignature.setBackgroundColor(Color.WHITE);
  // Dynamically generating Layout through java code
  mContent.addView(mSignature, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
  mClear = (Button) dialog.findViewById(R.id.clear);
  mGetSign = (Button) dialog.findViewById(R.id.getsign);
  mGetSign.setEnabled(false);
  mCancel = (Button) dialog.findViewById(R.id.cancel);
  view = mContent;

  mClear.setOnClickListener(new View.OnClickListener() {
   public void onClick(View v) {
    Log.v("tag", "Panel Cleared");
    mSignature.clear();
    mGetSign.setEnabled(false);
   }
  });
  mGetSign.setOnClickListener(new View.OnClickListener() {

   public void onClick(View v) {

    Log.v("tag", "Panel Saved");
    view.setDrawingCacheEnabled(true);
    mSignature.save(view, StoredPath);
    dialog.dismiss();
    Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();
    // Calling the same class
    recreate();
   }
  });
  mCancel.setOnClickListener(new View.OnClickListener() {
   public void onClick(View v) {
    Log.v("tag", "Panel Cancelled");
    dialog.dismiss();
    // Calling the same class
    recreate();
   }
  });
  dialog.show();
 }

 public class signature extends View {
  private static final float STROKE_WIDTH = 5f;
  private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
  private Paint paint = new Paint();
  private Path path = new Path();

  private float lastTouchX;
  private float lastTouchY;
  private final RectF dirtyRect = new RectF();

  public signature(Context context, AttributeSet attrs) {
   super(context, attrs);
   paint.setAntiAlias(true);
   paint.setColor(Color.BLACK);
   paint.setStyle(Paint.Style.STROKE);
   paint.setStrokeJoin(Paint.Join.ROUND);
   paint.setStrokeWidth(STROKE_WIDTH);
  }

  public void save(View v, String StoredPath) {
   Log.v("tag", "Width: " + v.getWidth());
   Log.v("tag", "Height: " + v.getHeight());
   if (bitmap == null) {
    bitmap = Bitmap.createBitmap(mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565);
   }
   Canvas canvas = new Canvas(bitmap);
   try {
    // Output the file
    FileOutputStream mFileOutStream = new FileOutputStream(StoredPath);
    v.draw(canvas);
    // Convert the output file to Image such as .png
    bitmap.compress(Bitmap.CompressFormat.PNG, 90, mFileOutStream);
    mFileOutStream.flush();
    mFileOutStream.close();
   } catch (Exception e) {
    Log.v("log_tag", e.toString());
   }
  }

  public void clear() {
   path.reset();
   invalidate();
  }

  @Override
  protected void onDraw(Canvas canvas) {
   canvas.drawPath(path, paint);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
   float eventX = event.getX();
   float eventY = event.getY();
   mGetSign.setEnabled(true);

   switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
     path.moveTo(eventX, eventY);
     lastTouchX = eventX;
     lastTouchY = eventY;
     return true;

    case MotionEvent.ACTION_MOVE:
 
    case MotionEvent.ACTION_UP:
     resetDirtyRect(eventX, eventY);
     int historySize = event.getHistorySize();
     for (int i = 0; i < historySize; i++) {
      float historicalX = event.getHistoricalX(i);
      float historicalY = event.getHistoricalY(i);
      expandDirtyRect(historicalX, historicalY);
      path.lineTo(historicalX, historicalY);
     }
     path.lineTo(eventX, eventY);
     break;
    default:
     debug("Ignored touch event: " + event.toString());
     return false;
   }

   invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH),
     (int) (dirtyRect.top - HALF_STROKE_WIDTH),
     (int) (dirtyRect.right + HALF_STROKE_WIDTH),
     (int) (dirtyRect.bottom + HALF_STROKE_WIDTH));

    lastTouchX = eventX;
    lastTouchY = eventY;

    return true;
  }

  private void debug(String string) {
   Log.v("log_tag", string);
  }

  private void expandDirtyRect(float historicalX, float historicalY) {
   if (historicalX < dirtyRect.left) {
    dirtyRect.left = historicalX;
   } else if (historicalX > dirtyRect.right) {
    dirtyRect.right = historicalX;
   }

   if (historicalY < dirtyRect.top) {
    dirtyRect.top = historicalY;
   } else if (historicalY > dirtyRect.bottom) {
    dirtyRect.bottom = historicalY;
   }
  }

  private void resetDirtyRect(float eventX, float eventY) {
   dirtyRect.left = Math.min(lastTouchX, eventX);
   dirtyRect.right = Math.max(lastTouchX, eventX);
   dirtyRect.top = Math.min(lastTouchY, eventY);
   dirtyRect.bottom = Math.max(lastTouchY, eventY);
  }
 }
}

Screen Shots

Screen 1
Screen 2
Screen 3

Download Full Source Code

You can download the full source from the following Github link. If you Like this tutorial, Please star it in Github.

Download From Github

Post your doubts and comments in the comments section.  

59 comments:

  1. Thank You Bro..Very Helpful Perfectly working

    ReplyDelete
    Replies
    1. Thanks Bro for your support and keep on visiting

      Delete
  2. Bro there´s a problem with the save function, the first time it works ok and save the sign in the specified folder, but from the second time the function doesn't create the file, what could be the problem, thanks for the tuto, it help me a lot! :)

    ReplyDelete
  3. How may I attach the saved file to an e-mail?

    ReplyDelete
    Replies
    1. You can attach the saved file by using Email Intent as in the following

      "Uri path = Uri.parse("file://" + filepath);
      Intent emailIntent = new Intent(Intent.ACTION_SEND);
      // set the type to 'email'
      emailIntent .setType("image/*");
      String to[] = {"myemail@gmail.com"};
      emailIntent .putExtra(Intent.EXTRA_EMAIL, to);
      // the attachment
      emailIntent .putExtra(Intent.EXTRA_STREAM, path);
      // the mail subject
      emailIntent .putExtra(Intent.EXTRA_SUBJECT, "Subject");
      startActivity(Intent.createChooser(emailIntent , "Send email..."));"

      Thanks for your support and Keep on Visiting

      Delete
  4. Well done ....bro.

    But how can I add the the image file to volley http request to be send to a server.
    Will appreciate any help.

    ReplyDelete
    Replies
    1. Convert your Image into Base64 string and pass it using volley

      Bitmap bm = BitmapFactory.decodeFile("/path/to/image.jpg");
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); //bm is the bitmap object
      byte[] byteArrayImage = baos.toByteArray();

      String encodedImage = Base64.encodeToString(byteArrayImage, Base64.DEFAULT);

      Refer this Link:http://stackoverflow.com/questions/4830711/how-to-convert-a-image-into-base64-string

      Delete
  5. This Tutorial is very Useful..Thanks for uploading.
    Now I want to display the image what i have captured..can you please help me out?

    ReplyDelete
    Replies
    1. Thanks for your support Swapna. You can display the captured image by setting the saved image path in ImageView as in the following

      ImageView preview = (ImageView) findViewById(R.id.preview);
      preview.setImageURI(Uri.parse(filePath));

      Delete
  6. pay with their phones in stores and speed through checkout in Android apps. apk download

    ReplyDelete
  7. i cant GET image on sd card on specified folder

    ReplyDelete
    Replies
    1. Are you got any exception related to your app?

      Maybe Permission Problem, if you used Android M.

      Check that and comment me if the error will not be solved.

      Delete
  8. I was facing file not found exception in your project while using M.

    Solution: Add the askForPermission(Manifest.permission.READ_EXTERNAL_STORAGE,READ_EXST);

    private void askForPermission(String permission, Integer requestCode) {
    if (ContextCompat.checkSelfPermission(MainActivity.this, permission) != PackageManager.PERMISSION_GRANTED) {

    if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission)) {

    //This is called if user has denied the permission before
    //In this case I am just asking the permission again
    ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);

    } else {

    ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);
    }
    } else {
    createDirectory();
    // Toast.makeText(this, "" + permission + " is already granted.", Toast.LENGTH_SHORT).show();
    }
    }

    handle it

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    Log.v("onRequestpermission", requestCode + "//");
    if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) {
    switch (requestCode) {
    //Location
    case 4:
    createDirectory();
    }

    } else {
    Log.v("Permission Denied", requestCode + "else//");
    }

    }

    Though the code for file and directory is not required but for reference i am putting it.

    private File createFile(String url){
    String filename = String.valueOf(url.hashCode());
    // String filename = URLEncoder.encode(url);
    File f = new File(DIRECTORY, filename);
    if(f.exists()) {
    return f;
    }
    else {
    try {
    f.createNewFile();
    } catch (IOException e){
    Log.v("File IOException", e.getMessage());
    }

    }
    return f;
    }

    // Method to create Directory, if the Directory doesn't exists
    private void createDirectory(){
    file = new File(DIRECTORY);
    if (!file.exists()) {
    file.mkdirs();
    }
    }

    Happy Coding :)

    ReplyDelete
    Replies
    1. few more lines of code in save method.
      // it will check the file is created or not.
      createDirectory();
      createFile(StoredPath);
      FileOutputStream mFileOutStream = new FileOutputStream(StoredPath);

      Delete
  9. can you show how can i save it to an online database?

    ReplyDelete
    Replies
    1. Dude i have a little problem it says that "Result of 'File.mkdir()' is ignored" will you help me how to resolve this problem

      Delete
  10. Bro, it's just a warning. 'File.mkdir()' returns boolean value. you can assign it to any boolean variable and print it with Logcat Like below
    Log.v("isDirectoryCreated",String.valueof(File.mkdir())).
    But it not an error.

    ReplyDelete
  11. HELLO GUYS I WANT TO ASK A QUESTION AND IF SOMEBODY CAN HELP..... I HAVE FIXED MY OWN APPLICATION AND I HAVE ADDED IN A SIGNATURE CANVAS IT WORKS ALL PERFECT, THE BITMAP IS SAVED PERFECT AT MY DATABASE BUT I HAVE A PROBLEM WHEN MY APPLICATION IS AT A BIG RESOLUTION PHONE LIKE GALAXY NOTE 4 (1440 x 2560 pixels (~518 ppi pixel density)) MY SIGNATURE BITMAP IS BEEN SAVED PERFECT BUT ITS VERY BIG AND HALF OF THE BITMAP IS GONE TO THE RIGHT HAW CAN I PUT MY BITMAP TO BE SAVED ALWAYS FOR EXAMPLE 1000X400 PIXELS OR 260 DPI AND NOT HAVING ANY PROBLEM OF WHAT PHONE USES MY APP PIXELS OR DENSITY?????IF SOMEBODY CAN HELP IT WILL MEAN A LOT FOR ME !!

    ReplyDelete
  12. i am totally beginner in android studio. and trying this code. but i cant run the app. i didn't know what happen like i said i am totally new with this and trying this out for self learning purpose. i run it on avd and its prompt a message that "unfortunately the app stop".How can i fix it? owh..and how im going to set it into default activity, maybe i attach the wrong one i guess.

    ReplyDelete
  13. How to convert this file to Base64?

    ReplyDelete
    Replies
    1. Hi,

      You can use the following snippets to convert Image to Base64 and Vice-Versa.

      // Image to Base64
      Bitmap bm = BitmapFactory.decodeFile("/path/to/image.jpg");
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); //bm is the bitmap object
      byte[] b = baos.toByteArray();
      String encodedImage = Base64.encodeToString(byteArrayImage, Base64.DEFAULT)

      // Base64 to Image
      byte[] decodedString = Base64.decode(encodedImage, Base64.DEFAULT);
      Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);

      Delete
  14. Hi.. Can you please help me, sir ? I have an email app. I want to send the last sign I created with a text.. but when I select the email app to send the image it's dosen't appear. What can I do ?

    ReplyDelete
    Replies
    1. Hey Bro,

      Use the following snippet to attach Image

      Intent intent = new Intent(Intent.ACTION_SEND);
      intent.setType("text/plain");
      intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"email@example.com"});
      intent.putExtra(Intent.EXTRA_SUBJECT, "subject here");
      intent.putExtra(Intent.EXTRA_TEXT, "body text");
      File root = Environment.getExternalStorageDirectory();
      File file = new File(root, xmlFilename);
      if (!file.exists() || !file.canRead()) {
      Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show();
      finish();
      return;
      }
      Uri uri = Uri.parse("file://" + file);
      intent.putExtra(Intent.EXTRA_STREAM, uri);
      startActivity(Intent.createChooser(intent, "Send email..."));

      Delete
  15. hi
    application is working fine if i change the tools.context:.main instead of @strings/app_name.



    http://schemas.android.com/tools"">


    and can you please help me in terms of path where this signature is getting saved.
    Thanks in advance.

    ReplyDelete
  16. Super Tutorial it is very Useful..Thanks for uploading.

    Now I want to display the image what i have captured..can you please help me out?

    ReplyDelete
  17. Hi, after reading this amazing paragraph i am also glad to share my
    familiarity here with friends.

    ReplyDelete
  18. Hi,this tutorial is working,But issue are raise for me,issue are when i first time sign clear function is worked,then exit the dialog. Second time i open signature dialog,I signed in layout but clear function is not worked,this is my problem please help me..?

    ReplyDelete
  19. create and generate your custom e-signatures live using our online signature maker tool. Create your own personal signature which you can use on Websites, Blogs, E-mails, Forums, Word Docs, PDFs, etc. Choose your own signature font and style to create and generate a perfect digital signature.
    https://www.signaturemaker.in/en/online-signature-maker/

    ReplyDelete
  20. nice tutorials to Create a Digital Signature Application in Android. thank you very much

    ReplyDelete
  21. Bro Thanku for sharing this awesome post with android begineers
    Bro I wanted to get entered file name by user in edittext and save this png with that entered file name
    how can I implement this
    Bro your help will be appreciated

    ReplyDelete
  22. I'm dazzled, I should state. In all respects infrequently do I run over a blog that is both useful and engaging, and let me let you know, you ve hit the nail on the head. Your blog is significant.. Fred @ LeReviewist

    ReplyDelete
  23. Where to find saved signatures in Phone?

    ReplyDelete
  24. Thanks for the post and great tips..even I also think that hard work is the most important aspect of getting success..
    app development company in mumbai

    ReplyDelete
  25. // // Function for Digital Signature
    mContent.addView( mSignature, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT );

    It brings this error below
    "error: no suitable method found for addView(dialog_signature,int,int)
    mContent.addView( mSignature, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT );"


    Please Help!!!!!

    ReplyDelete
  26. Particular interviews furnish firsthand message on mart size, industry trends, ontogeny trends, capitalist landscape and outlook, etc. agen asuransi allianz

    ReplyDelete
  27. Some casinos automatically credit your account with a birthday bonus, so keep in mind to log in and verify. It’s value noting that while no deposit bonuses are sometimes provided as part of of} a on-line casino registration bonus 카지노사이트 package deal, they’re not at all times reserved model spanking new|for model new} gamers. You may find that the casino additionally presents you a deposit bonus if you add extra funds too.

    ReplyDelete

Please Comment about the Posts and Blog