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.  

Share this

Related Posts

Previous
Next Post »

40 comments

comments
21 November 2015 at 12:31 delete

Good job bro. Very helpful.

Reply
avatar
Anonymous
22 October 2016 at 10:32 delete

Thank You Bro..Very Helpful Perfectly working

Reply
avatar
23 October 2016 at 21:43 delete

Thanks Bro for your support and keep on visiting

Reply
avatar
17 December 2016 at 06:52 delete

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! :)

Reply
avatar
Anonymous
19 January 2017 at 20:01 delete

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

Reply
avatar
1 February 2017 at 06:11 delete

Good Job. Work perfectly for me

Reply
avatar
4 February 2017 at 23:11 delete

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

Reply
avatar
1 March 2017 at 22:17 delete

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.

Reply
avatar
7 March 2017 at 21:30 delete

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

Reply
avatar
6 April 2017 at 10:40 delete

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?

Reply
avatar
8 April 2017 at 08:21 delete

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));

Reply
avatar
18 April 2017 at 19:05 delete

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

Reply
avatar
23 April 2017 at 18:54 delete

i cant GET image on sd card on specified folder

Reply
avatar
23 April 2017 at 20:31 delete

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.

Reply
avatar
26 April 2017 at 09:08 delete

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 :)

Reply
avatar
26 April 2017 at 09:12 delete

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);

Reply
avatar
18 May 2017 at 16:48 delete

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

Reply
avatar
20 May 2017 at 10:46 delete

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

Reply
avatar
20 May 2017 at 23:25 delete

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.

Reply
avatar
9 July 2017 at 04:20 delete

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 !!

Reply
avatar
1 August 2017 at 00:36 delete

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.

Reply
avatar
26 August 2017 at 08:49 delete

How to convert this file to Base64?

Reply
avatar
27 August 2017 at 11:15 delete

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);

Reply
avatar
27 August 2017 at 20:17 delete

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 ?

Reply
avatar
29 August 2017 at 21:31 delete

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..."));

Reply
avatar
Anonymous
18 June 2018 at 13:57 delete

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.

Reply
avatar
26 October 2018 at 16:40 delete

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?

Reply
avatar
25 November 2018 at 10:41 delete

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

Reply
avatar
1 December 2018 at 12:05 delete

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..?

Reply
avatar
4 January 2019 at 22:24 delete

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/

Reply
avatar
4 January 2019 at 22:26 delete

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

Reply
avatar
16 January 2019 at 12:54 delete

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

Reply
avatar
18 September 2019 at 15:42 delete

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

Reply
avatar
17 December 2019 at 17:51 delete

Where to find saved signatures in Phone?

Reply
avatar
1 January 2020 at 17:44 delete

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

Reply
avatar
27 August 2020 at 01:13 delete

// // 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!!!!!

Reply
avatar

Please Comment about the Posts and Blog