Thursday, 9 April 2015

Android Pattern Lock Tutorial

Hi Folks, In this post i am going to show you how to create a Lock9View and create your own pattern as we have seen in Android Pattern Lock Screen. By following this tutorial you can use this Pattern Lock in you application as a security test for users before performing any task or passing to an activity.

Source code from : http://android-arsenal.com/details/1/1790

Step 1 : Create a new project by going to File ⇒ New Android Application Project. Fill all the details and name your activity as MainActivity.

⇒ Designing pattern node

The next major step is to implement the Lock9View into our project. Before that we should place the drawables for pattern node.
  • lock_9_view_node_highlighted.png
  • lock_9_view_node_normal.png

Step 2 : Create a new xml file under values. Right Click on values folder ⇒ New ⇒ Android XML File and name it as attrs_lock_9_view.xml and fill it with following code.

attrs_lock_9_view.xml
<?xml version = "1.0" encoding = "utf-8"?>
<resources>
    <declare-styleable name = "Lock9View">   
 
                <attr name = "nodeSrc" format = "color|reference">
        <attr name = "nodeOnSrc" format = "color|reference">
        <attr name = "lineColor" format = "color">
        <attr name = "lineWidth" format = "dimension"> 
       </declare-styleable>
</resources>
 
The declare-styleable is used to set attributes to components. We have declared style attributes for Lock9View in attrs_lock_9_view.xml as shown above. These style attributes can be set in xml where the Lock9View is placed.

Step 3 : Create a package under main package. Right Click on main package ⇒ New ⇒ Package and name after your main package as .utils.
[for eg : My main package is : com.android.androiddelight.lock9view Utils package is : com.android.androiddelight.lock9view.utils]

Step 4 : Create a new class under src/utils package. Right Click on src/utils package (com.android.androiddelight.lock9view.utils) ⇒ New ⇒ Class and name it as Lock9View.java and fill it with following code.
                                           Lock9View.java
package com.android.androiddelight.lock9view.utils;

import java.util.ArrayList;
import java.util.List;

import com.android.androiddelight.lock9view.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

public class Lock9View extends ViewGroup {
  private Paint paint;
  private Bitmap bitmap;
  private Canvas canvas;

  private List<Pair<NodeView, NodeView>> lineList;
  private NodeView currentNode;
  
  private StringBuilder pwdSb;
  private CallBack callBack;
 
  private Drawable nodeSrc;
  private Drawable nodeOnSrc;
public Lock9View(Context context) {
        this(context, null);
    }
public Lock9View(Context context, AttributeSet attrs) {
        this(context, attrs,
0);
    }
public Lock9View(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,
0);
    }
public Lock9View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr); 
        initFromAttributes(attrs, defStyleAttr);
    }
private void initFromAttributes(AttributeSet attrs, int defStyleAttr) {
        final TypedArray a = getContext().obtainStyledAttributes(attrs,    R.styleable.Lock9View, defStyleAttr,
0);

        nodeSrc = a.getDrawable(R.styleable.Lock9View_nodeSrc);
        nodeOnSrc = a.getDrawable(R.styleable.Lock9View_nodeOnSrc);
        int lineColor = Color.argb(
0, 0, 0, 0);
        lineColor = a.getColor(R.styleable.Lock9View_lineColor, lineColor);
        float lineWidth = 20.0f;
        lineWidth = a.getDimension(R.styleable.Lock9View_lineWidth, lineWidth);

        a.recycle();

        paint = new Paint(Paint.DITHER_FLAG);
        paint.setStyle(Style.STROKE);
        paint.setStrokeWidth(lineWidth);
        paint.setColor(lineColor);
        paint.setAntiAlias(true);

        DisplayMetrics dm = getResources().getDisplayMetrics();
        bitmap = Bitmap.createBitmap(dm.widthPixels, dm.widthPixels,   Bitmap.Config.ARGB_8888);
        canvas = new Canvas();
        canvas.setBitmap(bitmap);

        for (int n = 0; n < 9; n++) {
            NodeView node = new NodeView(getContext(), n + 1);
            addView(node);
        }
        lineList = new ArrayList<Pair<NodeView,NodeView>>();
        pwdSb = new StringBuilder();

        setWillNotDraw(false);
    } 

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
    }
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (!changed) {
            return;
        }
        int width = right - left;
        int nodeWidth = width / 3;
        int nodePadding = nodeWidth / 6;
        for (int n = 0; n < 9; n++) {
            NodeView node = (NodeView) getChildAt(n);
            int row = n / 3;
            int col = n % 3;
            int l = col * nodeWidth + nodePadding;
            int t = row * nodeWidth + nodePadding;
            int r = col * nodeWidth + nodeWidth - nodePadding;
            int b = row * nodeWidth + nodeWidth - nodePadding;
            node.layout(l, t, r, b);
        }
    }
@Override 
protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(bitmap, 0, 0, null);
    }
@Override 
protected boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                NodeView nodeAt = getNodeAt(event.getX(), event.getY());
                if (nodeAt == null && currentNode == null) {
                    return true;
                } else {
                    clearScreenAndDrawList();
                    if (currentNode == null) {
                        currentNode = nodeAt;
                        currentNode.setHighLighted(true);
                        pwdSb.append(currentNode.getNum());
                    }
                    else if (nodeAt == null || nodeAt.isHighLighted()) {
                        
                        canvas.drawLine(currentNode.getCenterX(), currentNode.getCenterY(), event.getX(), event.getY(), paint);
                    } else {
                        canvas.drawLine(currentNode.getCenterX(), currentNode.getCenterY(), nodeAt.getCenterX(), nodeAt.getCenterY(), paint);                        
            nodeAt.setHighLighted(true);
            Pair<NodeView, NodeView> pair = new Pair<NodeView, NodeView>(currentNode, nodeAt);
            lineList.add(pair);
                       
             currentNode = nodeAt;
             pwdSb.append(currentNode.getNum());
         }
             invalidate();
         }
         return true;
         
         case MotionEvent.ACTION_UP:
              
                if (pwdSb.length() <= 0) {
                    return super.onTouchEvent(event);
                }
             
                if (callBack != null) {
                    callBack.onFinish(pwdSb.toString());
                    pwdSb.setLength(0);
                }
              
                currentNode =
null;
                lineList.clear();
                clearScreenAndDrawList();
               
                for (int n = 0; n < getChildCount(); n++) {
                    NodeView node = (NodeView) getChildAt(n);
                    node.setHighLighted(false);
                }
               
                invalidate();
                return true;
        }
        return super.onTouchEvent(event);
    }

private void clearScreenAndDrawList() {
     canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
     for (Pair<NodeView, NodeView> pair : lineList) {
          canvas.drawLine(pair.first.getCenterX(), pair.first.getCenterY(),  pair.second.getCenterX(), pair.second.getCenterY(), paint);
        }
    }
private NodeView getNodeAt(float x, float y) {
        
          for (int n = 0; n < getChildCount(); n++) {
            NodeView node = (NodeView) getChildAt(n);
            if (!(x >= node.getLeft() && x < node.getRight())) {
                continue;
            }
            if (!(y >= node.getTop() && y < node.getBottom())) {
               
continue;
            }
            return node;
        }
        return null;
    }
public void setCallBack(CallBack callBack) {
        this.callBack = callBack;
    }

public class NodeView extends View {

        private int num;
       
private boolean highLighted;

       
private NodeView(Context context) {
            super(context);
        }

       
public NodeView(Context context, int num) {
            this(context);
            this.num = num;
            highLighted = false;
            if (nodeSrc == null) {
                setBackgroundResource(0);
            } else {
                setBackgroundDrawable(nodeSrc);
            }
        }

       
public boolean isHighLighted() {
            return highLighted;
        }

       
public void setHighLighted(boolean highLighted) {
            this.highLighted = highLighted;
            if (highLighted) {
                if (nodeOnSrc == null) {
                    setBackgroundResource(0);
                } else {
                    setBackgroundDrawable(nodeOnSrc);
                }
            } else {
                if (nodeSrc == null) {
                    setBackgroundResource(0);
                } else {
                    setBackgroundDrawable(nodeSrc);
                }
            }
        }

        public int getCenterX() {
            return (getLeft() + getRight()) / 2;
        }

        public int
getCenterY() {
           
return (getTop() + getBottom()) / 2;
        }

        public int getNum() {
           
return num;
        }

        public void setNum(int num) {
            this.num = num;
        }
    }
public interface CallBack {
        public void onFinish(String password);
    }
}

Now we are ready with the 9 Pattern View. Next i am going to declare this view inside my main layout that is activity_main.xml.

Step 5 : Open the activity_main.xml under res/layout and fill it with following code.

activity_main.xml

<?xml version = "1.0" encoding = "utf-8"?>
<FrameLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    xmlns:app = "http://schemas.android.com/apk/res-auto"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    android:background = "#ff222233">
      <!--Include the Lock9View along with the package where it is located -->
      <com.android.androiddelight.lock9view.utils.Lock9View
        android:id = "@+id/lock_9_view"
        android:layout_width = "match_parent"
        android:layout_height = "wrap_content"
        android:layout_gravity = "center"
        <!--Note : These  attributes are from declare-styleable -->
        app:nodeSrc = "@drawable/lock_9_view_node_normal"
        app:nodeOnSrc = "@drawable/lock_9_view_node_highlighted"
        app:lineColor = "#ff006699"
        app:lineWidth = "8dp" />

</FrameLayout

Step 6 : Open the MainActivity.java under src/package we need to declare the Lock9View and reference it in MainActivity. Write the code as follows in MainActivity.

MainActivity.java
package com.android.androiddelight.lock9view;

import com.android.androiddelight.lock9view.utils.Lock9View;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

private Lock9View lock9View;
private static String MY_PREFS_NAME = "PatternLock";
private static String PATTERN_KEY; 
 
SharedPreferences prefs;

@Override
public void onCreate(Bundle savedInstanceState){
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
   
   lock9View = (Lock9View) findViewById(R.id.lock_9_view);
   lock9View.setCallBack(new Lock9View.CallBack() {

      @Override
     
public void onFinish(String password) {
        PATTERN_KEY = prefs.getString("Pattern", "invalid");
               
        if(PATTERN_KEY.equals("invalid")){
        Toast.makeText(MainActivity.this, "Options --> Create new Pattern", Toast.LENGTH_LONG).show();   
        }else{
        if(password.equals(PATTERN_KEY)){
           Intent intent = new Intent(MainActivity.this, WelcomeActivity.class);
           startActivity(intent);
           finish();
           Toast.makeText(MainActivity.this, "Login success!",   Toast.LENGTH_SHORT).show();
           }else{
           Toast.makeText(MainActivity.this, "Pattern incorrect!", Toast.LENGTH_SHORT).show();
                    }
                } 
            }
        });

@Override
public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

@Override
public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.contact:
                new AlertDialog.Builder(this)
                .setTitle("Android-Lock9View")
                .setMessage(
                "Email : anfersyed@gmail.com\n" +
                "Blog  : http://android-delight.blogspot.in/\n")
                .setPositiveButton("OK", null)
                .show();
                return true;
               
            case R.id.create_new_pattern:
            Intent intent = new Intent(MainActivity.this, ChangeActivity.class);
                startActivity(intent);
                finish();
                return true;
               
            default:
            return super.onOptionsItemSelected(item);
        }
    }
}

Step 7 : We have to create one more activity to change the pattern. So Create a new Activity by going to File ⇒ New Other ⇒ Android Activity. Fill all the details and name your activity as ChangeActivity.

In ChangeActivity we need 2 Lock9View 1- for drawing new pattern 2 - for confirm drawing pattern
 
Step 8 : Open the activity_change.xml under res/layout and fill it with following code.

activity_change.xml
<?xml version = "1.0" encoding = "utf-8"?>
<FrameLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    xmlns:app = "http://schemas.android.com/apk/res-auto"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    android:background = "#ff222233">
    <!--TextView for prompting the users  -->
      <TextView
        android:id = "@+id/tvMsg"
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:layout_marginLeft = "5dp"
        android:layout_marginTop = "20dp"
        android:textAppearance = "?android:attr/textAppearanceSmall"
        android:textColor = "#ff006699" />
      <!-- FrameLayout to hold the 1st Lock9View -->
      <FrameLayout
        android:id = "@+id/enterPattern"
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:layout_gravity = "center" >
 
      <com.android.androiddelight.lock9view.utils.Lock9View
        android:id = "@+id/lock_9_view"
        android:layout_width = "match_parent"
        android:layout_height = "wrap_content"
        android:layout_gravity = "center"
        app:nodeSrc = "@drawable/lock_9_view_node_normal"
        app:nodeOnSrc = "@drawable/lock_9_view_node_highlighted"
        app:lineColor = "#ff006699"
        app:lineWidth = "8dp" />
     
      </FrameLayout>
      <!-- FrameLayout to hold the 2nd Lock9View -->
      <FrameLayout
        android:id = "@+id/confirmPattern"
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:layout_gravity = "center" 
        android:visibility = "gone" >
 
      <com.android.androiddelight.lock9view.utils.Lock9View
        android:id = "@+id/lock_viewConfirm"
        android:layout_width = "match_parent"
        android:layout_height = "wrap_content"
        android:layout_gravity = "center"

        app:nodeSrc = "@drawable/lock_9_view_node_normal"
        app:nodeOnSrc = "@drawable/lock_9_view_node_highlighted"
        app:lineColor = "#ff006699"
        app:lineWidth = "8dp" />
      </FrameLayout> 
</FrameLayout

The layout is build. Now getting to java code to write business logic.

Step 9 : Open the ChangeActivity.java under src/package and write down the code as follows.

                                                           ChangeActivity.java

package com.android.androiddelight.lock9view;

import com.android.androiddelight.lock9view.utils.Lock9View;
import android.support.v7.app.ActionBarActivity;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;

public class ChangeActivity extends  ActionBarActivity {

private FrameLayout enterPatternContainer, confirmPatternContainer;
private Lock9View lockViewFirstTry, lockViewConfirm;
private static String MY_PREFS_NAME = "PatternLock";
private static String PATTERN_KEY;
private SharedPreferences prefs;private Editor editor;
   
private TextView tvMsg;
@Override
protected void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_change);
  prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
  editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
  
  tvMsg = (TextView)findViewById(R.id.tvMsg);
  tvMsg.setText(getResources().getString(R.string.draw_pattern_msg)); 
    
  enterPatternContainer = (FrameLayout) findViewById(R.id.enterPattern);
  confirmPatternContainer = (FrameLayout) findViewById(R.id.confirmPattern);

  lockViewFirstTry = (Lock9View) findViewById(R.id.lock_viewFirstTry);
  lockViewConfirm =  (Lock9View) findViewById(R.id.lock_viewConfirm);
//we can get a call back string when ever user interacts 
//with the pattern lock view
lockViewFirstTry.setCallBack(new Lock9View.CallBack() {

 
@Override  
  public void onFinish(String password) {
     PATTERN_KEY = password;
     enterPatternContainer.setVisibility(View.GONE);
     tvMsg.setText(getResources().getString
     (R.string.redraw_confirm_pattern_msg));
     confirmPatternContainer.setVisibility(View.VISIBLE);
  }
}); 

lockViewConfirm.setCallBack(new Lock9View.CallBack() {

 
@Override  
  public void onFinish(String password) {

    if(password.equals(PATTERN_KEY)){
    Toast.makeText(getApplicationContext(), 
    "Pattern created successfully!", 
    Toast.LENGTH_SHORT).show();
    editor.putString("Pattern", password);
    editor.commit();
    Intent intent = new Intent(ChangeActivity.this, MainActivity.class);
    startActivity(intent);
    finish();
    }else{
    Toast.makeText(getApplicationContext(), 
    "You have drawn the wrong Pattern", Toast.LENGTH_SHORT).show();
    }
   }
  });

@Override     
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_change, menu);
    return true;
    }

@Override     
public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {

        case R.id.contact:
            new AlertDialog.Builder(this)
            .setTitle("Android-Lock9View")
            .setMessage(
            "Email : anfersyed@gmail.com"
            +"\nBlog  : http://android-delight.blogspot.in/")
            .setPositiveButton("OK", null).show();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

Checkout the live demo :




Click here to get the source code. Share it!

No comments:

Post a Comment