xuxiuxi
2017-07-10 d6119c2d7fe2f802dd224d77bf2d95eeeedb4526
VisitFace/DemoForBsk/app/src/main/java/cn/com/basic/face/base/MainActivity.java
@@ -1,13 +1,47 @@
package cn.com.basic.face.base;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable;
import android.hardware.Camera;
import android.os.Handler;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.bsk.zhangbo.demoforbsk.R;
import cn.com.basic.face.adapter.MyFragmentPagerAdapter;
import cn.com.basic.face.fragment.SupervisoryFragment;
import com.luxand.FSDK;
import cn.com.basic.face.discern.common.CommonVariables;
import cn.com.basic.face.discern.query.item.SurveillanceQueryItem;
import cn.com.basic.face.fragment.SurveillanceFragment;
import cn.com.basic.face.service.SurveillanceMng;
import cn.com.basic.face.util.FileUtil;
import cn.com.basic.face.util.NativeImg;
import cn.com.basic.face.util.TabNameUtil;
import cn.com.basic.face.fragment.AttendanceFragment;
import cn.com.basic.face.fragment.CheckInFragment;
@@ -16,10 +50,17 @@
import cn.com.basic.face.fragment.RegisterFragment;
import cn.com.basic.face.fragment.VisitFragment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MainActivity extends BaseActivity {
public class MainActivity extends BaseActivity implements View.OnClickListener{
    private static MainActivity instance;
@@ -48,27 +89,800 @@
        List<String> tabNamesList = Arrays.asList(TabNameUtil.tabNames);
        int[] tabIcons = TabNameUtil.tabIcons;
        MyFragmentPagerAdapter myFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),MainActivity.this);
        myFragmentPagerAdapter.addFragment(SupervisoryFragment.getInstance(), tabNamesList.get(0),tabIcons[0]);
        myFragmentPagerAdapter.addFragment(SurveillanceFragment.getInstance(), tabNamesList.get(0),tabIcons[0]);
        myFragmentPagerAdapter.addFragment(RegisterFragment.getInstance(), tabNamesList.get(1),tabIcons[1]);
        myFragmentPagerAdapter.addFragment(CheckInFragment.getInstance(), tabNamesList.get(2),tabIcons[2]);
        myFragmentPagerAdapter.addFragment(PhoneCallFragment.newInstance(), tabNamesList.get(3),tabIcons[3]);
        myFragmentPagerAdapter.addFragment(VisitFragment.newInstance(), tabNamesList.get(4),tabIcons[4]);
        myFragmentPagerAdapter.addFragment(AttendanceFragment.newInstance(), tabNamesList.get(5),tabIcons[5]);
        myFragmentPagerAdapter.addFragment(DeviceFragment.newInstance(), tabNamesList.get(6),tabIcons[6]);
        myFragmentPagerAdapter.addFragment(PhoneCallFragment.getInstance(), tabNamesList.get(3),tabIcons[3]);
        myFragmentPagerAdapter.addFragment(VisitFragment.getInstance(), tabNamesList.get(4),tabIcons[4]);
        myFragmentPagerAdapter.addFragment(AttendanceFragment.getInstance(), tabNamesList.get(5),tabIcons[5]);
        myFragmentPagerAdapter.addFragment(DeviceFragment.getInstance(), tabNamesList.get(6),tabIcons[6]);
        mViewPager.setAdapter(myFragmentPagerAdapter);
        mViewPager.setOffscreenPageLimit(6);//viewpager缓存个数
        mTabLayout.setupWithViewPager(mViewPager);
        mTabLayout.setTabMode(TabLayout.MODE_FIXED);
        for (int i = 0; i < mTabLayout.getTabCount(); i++) {
            TabLayout.Tab tab = mTabLayout.getTabAt(i);
            tab.setCustomView(myFragmentPagerAdapter.getTabView(i));
            LinearLayout view = (LinearLayout)myFragmentPagerAdapter.getTabView(i);
            tab.setCustomView(view);
            if (i == 0) {
                tab.getCustomView().setSelected(true);
            }
        }
        myThread = new MyThread();
        myThread.start();
    }
    public static Context getInstance() {
    public static MainActivity getInstance() {
        return instance;
    }
    public static class MyFragmentPagerAdapter extends FragmentPagerAdapter {
        private final List<Fragment> mFragments = new ArrayList<>();
        private final List<String> mFragmentTitles = new ArrayList<>();
        private final List<Integer> mFragmentIcons = new ArrayList<>();
        private Context context;
        public MyFragmentPagerAdapter(FragmentManager fm, Context context) {
            super(fm);
            this.context = context;
        }
        public void addFragment(Fragment fragment, String title,int drawable) {
            mFragments.add(fragment);
            mFragmentTitles.add(title);
            mFragmentIcons.add(drawable);
        }
        @Override
        public Fragment getItem(int position) {
            return mFragments.get(position);
        }
        @Override
        public int getCount() {
            return mFragments.size();
        }
        @Override
        public CharSequence getPageTitle(int position) {
            return null;
        }
        public View getTabView(int position){
            View view = LayoutInflater.from(context).inflate(R.layout.activity_main_tab,null);
            TextView mTvTitle = (TextView) view.findViewById(R.id.tab_item_title);
            mTvTitle.setText(mFragmentTitles.get(position));
            ImageView mImgIcon = (ImageView) view.findViewById(R.id.tab_item_icon);
            mImgIcon.setImageResource(mFragmentIcons.get(position));
            return view;
        }
    }
    boolean doubleBackToExitPressedOnce = false;
    @Override
    public void onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            return;
        }
        this.doubleBackToExitPressedOnce = true;
        Toast.makeText(this, "请再次按返回键退出", Toast.LENGTH_SHORT).show();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                doubleBackToExitPressedOnce=false;
            }
        }, 2000);
    }
    private MyThread myThread;
    class MyThread extends Thread{
        @Override
        public void run() {
            try {
                setBmpDecodeEnv();
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 将raw下的文件复制到sdCard
     */
    public boolean setBmpDecodeEnv() {
        String path=getFilesDir().getAbsolutePath();
        String filename = "base.dat";
        try{
            String databaseFilename = path + "/" + filename;
            File dir = new File(path);
            if (!dir.exists())
                dir.mkdir();
            if (!(new File(databaseFilename)).exists()){
                InputStream is = getResources().openRawResource(R.raw.base);
                FileOutputStream fos = new FileOutputStream(databaseFilename);
                byte[] buffer = new byte[8192];
                int count = 0;
                while ((count = is.read(buffer)) > 0)
                {
                    fos.write(buffer, 0, count);
                }
                fos.close();
                is.close();
            }
        }
        catch (Exception e){
            System.out.println(e.getMessage());
            return false;
        }
        String filename1 = "license.lic";
        try{
            String databaseFilename = path + "/" + filename1;
            File dir = new File(path);
            if (!dir.exists())
                dir.mkdir();
            if (!(new File(databaseFilename)).exists()){
                InputStream is = getResources().openRawResource(R.raw.license);
                FileOutputStream fos = new FileOutputStream(databaseFilename);
                byte[] buffer = new byte[8192];
                int count = 0;
                while ((count = is.read(buffer)) > 0)
                {
                    fos.write(buffer, 0, count);
                }
                fos.close();
                is.close();
            }
        }
        catch (Exception e){
            System.out.println(e.getMessage());
            return false;
        }
        return true;
    }
    ////////////////////////////////////////////////////////
    private boolean mIsFailed = false;
    public Preview mPreview;
    public ProcessImageAndDrawResults mDraw;
    private final String database = "Memory50.dat";
    private final String help_text = "Luxand Face Recognition\n\nJust tap any detected face and name it. The app will recognize this face further. For best results, hold the device at arm's length. You may slowly rotate the head for the app to memorize you at multiple views. The app can memorize several persons. If a face is not recognized, tap and name it again.\n\nThe SDK is available for mobile developers: www.luxand.com/facesdk";
    public static float sDensity = 1.0f;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (CommonVariables.FACE_SERVER) {
            sDensity = getResources().getDisplayMetrics().scaledDensity;
            int res = FSDK.ActivateLibrary("bSB3NdbTnv/0eW/uhypSe6hDMtjZ76Sisw5NwcN+0sfahxOtoUW22el54e/M6cSG5/xsdVIorPgugbTIfoIIn7ltyw1QMSleNebVx/Xe8aRA8bP+aVDybjoWdW/0rDP9Pv7yqBzNXyuwjgsVhPB53VGP8oTirTSUP7PTzSwOEe0=");
            if (res != FSDK.FSDKE_OK) {
                mIsFailed = true;
                showErrorAndClose("FaceSDK activation failed", res);
            } else {
                FSDK.Initialize();
                // Hide the window title (it is done in manifest too)
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//z                requestWindowFeature(Window.FEATURE_NO_TITLE);
                // Lock orientation
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                // Camera layer and drawing layer
                mDraw = new ProcessImageAndDrawResults(this);
                mPreview = new Preview(this, mDraw);
                mDraw.mTracker = new FSDK.HTracker();
                String templatePath = this.getApplicationInfo().dataDir + "/" + database;
                if (FSDK.FSDKE_OK != FSDK.LoadTrackerMemoryFromFile(mDraw.mTracker, templatePath)) {
                    res = FSDK.CreateTracker(mDraw.mTracker);
                    if (FSDK.FSDKE_OK != res) {
                        showErrorAndClose("Error creating tracker", res);
                    }
                }
                resetTrackerParameters();
                //this.getWindow().setBackgroundDrawable(new ColorDrawable()); //black background
                //setContentView(mPreview); //creates MainActivity contents
                //addContentView(mDraw, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                // Menu
//                LayoutInflater inflater = (LayoutInflater)this.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
//                View buttons = inflater.inflate(R.layout.bottom_menu, null );
//                buttons.findViewById(R.id.helpButton).setOnClickListener(this);
//                buttons.findViewById(R.id.clearButton).setOnClickListener(this);
//                addContentView(buttons, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            }
        }
    }
    @Override
    public void onClick(View view) {
        if(CommonVariables.FACE_SERVER) {
            if (view.getId() == R.id.helpButton) {
                showMessage(help_text);
            } else if (view.getId() == R.id.clearButton) {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage("Are you sure to clear the memory?" )
                        .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                            @Override public void onClick(DialogInterface dialogInterface, int j) {
                                pauseProcessingFrames();
                                FSDK.ClearTracker(mDraw.mTracker);
                                resetTrackerParameters();
                                resumeProcessingFrames();
                            }
                        })
                        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                            @Override public void onClick(DialogInterface dialogInterface, int j) {
                            }
                        })
                        .setCancelable(false) // cancel with button only
                        .show();
            }
        }
    }
    @Override
    protected void onPause() {
        super.onPause();
        if(CommonVariables.FACE_SERVER) {
            pauseProcessingFrames();
            String templatePath = this.getApplicationInfo().dataDir + "/" + database;
            FSDK.SaveTrackerMemoryToFile(mDraw.mTracker, templatePath);
        }
    }
    @Override
    protected void onResume() {
        super.onResume();
        if(CommonVariables.FACE_SERVER) {
            if (mIsFailed)
                return;
            resumeProcessingFrames();
        }
    }
    public void showErrorAndClose(String error, int code) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(error + ": " + code)
                .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        android.os.Process.killProcess(android.os.Process.myPid());
                    }
                })
                .show();
    }
    public void showMessage(String message) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(message)
                .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                    }
                })
                .setCancelable(false) // cancel with button only
                .show();
    }
    private void resetTrackerParameters() {
        int errpos[] = new int[1];
        FSDK.SetTrackerMultipleParameters(mDraw.mTracker, "ContinuousVideoFeed=true;FacialFeatureJitterSuppression=0;RecognitionPrecision=0;Threshold=0.996;Threshold2=0.9995;ThresholdFeed=0.97;MemoryLimit=1000;HandleArbitraryRotations=false;DetermineFaceRotationAngle=false;InternalResizeWidth=70;FaceDetectionThreshold=5;", errpos);
        if (errpos[0] != 0) {
            showErrorAndClose("Error setting tracker parameters, position", errpos[0]);
        }
    }
    private void pauseProcessingFrames() {
        mDraw.mStopping = 1;
        // It is essential to limit wait time, because mStopped will not be set to 0, if no frames are feeded to mDraw
        for (int i=0; i<100; ++i) {
            if (mDraw.mStopped != 0) break;
            try { Thread.sleep(10); }
            catch (Exception ex) {}
        }
    }
    private void resumeProcessingFrames() {
        mDraw.mStopped = 0;
        mDraw.mStopping = 0;
    }
}
class FaceRectangle {
    public int x1, y1, x2, y2;
}
// Draw graphics on top of the video
class ProcessImageAndDrawResults extends View {
    public FSDK.HTracker mTracker;
    final int MAX_FACES = 5;
    final FaceRectangle[] mFacePositions = new FaceRectangle[MAX_FACES];
    final long[] mIDs = new long[MAX_FACES];
    final Lock faceLock = new ReentrantLock();
    int mTouchedIndex;
    long mTouchedID;
    int mStopping;
    int mStopped;
    Context mContext;
    Paint mPaintGreen, mPaintBlue, mPaintBlueTransparent;
    byte[] mYUVData;
    byte[] mRGBData;
    int mImageWidth, mImageHeight;
    boolean first_frame_saved;
    boolean rotated;
    int GetFaceFrame(FSDK.FSDK_Features Features, FaceRectangle fr)
    {
        if (Features == null || fr == null)
            return FSDK.FSDKE_INVALID_ARGUMENT;
        float u1 = Features.features[0].x;
        float v1 = Features.features[0].y;
        float u2 = Features.features[1].x;
        float v2 = Features.features[1].y;
        float xc = (u1 + u2) / 2;
        float yc = (v1 + v2) / 2;
        int w = (int)Math.pow((u2 - u1) * (u2 - u1) + (v2 - v1) * (v2 - v1), 0.5);
        fr.x1 = (int)(xc - w * 1.6 * 0.9);
        fr.y1 = (int)(yc - w * 1.1 * 0.9);
        fr.x2 = (int)(xc + w * 1.6 * 0.9);
        fr.y2 = (int)(yc + w * 2.1 * 0.9);
        if (fr.x2 - fr.x1 > fr.y2 - fr.y1) {
            fr.x2 = fr.x1 + fr.y2 - fr.y1;
        } else {
            fr.y2 = fr.y1 + fr.x2 - fr.x1;
        }
        return 0;
    }
    public ProcessImageAndDrawResults(Context context) {
        super(context);
        mTouchedIndex = -1;
        mStopping = 0;
        mStopped = 0;
        rotated = false;
        mContext = context;
        mPaintGreen = new Paint();
        mPaintGreen.setStyle(Paint.Style.FILL);
        mPaintGreen.setColor(Color.GREEN);
        mPaintGreen.setTextSize(18 * MainActivity.sDensity);
        mPaintGreen.setTextAlign(Paint.Align.CENTER);
        mPaintBlue = new Paint();
        mPaintBlue.setStyle(Paint.Style.FILL);
        mPaintBlue.setColor(Color.BLUE);
        mPaintBlue.setTextSize(18 * MainActivity.sDensity);
        mPaintBlue.setTextAlign(Paint.Align.CENTER);
        mPaintBlueTransparent = new Paint();
        mPaintBlueTransparent.setStyle(Paint.Style.STROKE);
        mPaintBlueTransparent.setStrokeWidth(2);
        mPaintBlueTransparent.setColor(Color.BLUE);
        mPaintBlueTransparent.setTextSize(25);
        //mBitmap = null;
        mYUVData = null;
        mRGBData = null;
        first_frame_saved = false;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        if (mStopping == 1) {
            mStopped = 1;
            super.onDraw(canvas);
            return;
        }
        if (mYUVData == null || mTouchedIndex != -1) {
            super.onDraw(canvas);
            return; //nothing to process or name is being entered now
        }
        int canvasWidth = canvas.getWidth();
        //int canvasHeight = canvas.getHeight();
        // Convert from YUV to RGB
        decodeYUV420SP(mRGBData, mYUVData, mImageWidth, mImageHeight);
        // Load image to FaceSDK
        FSDK.HImage Image = new FSDK.HImage();
        FSDK.FSDK_IMAGEMODE imagemode = new FSDK.FSDK_IMAGEMODE();
        imagemode.mode = FSDK.FSDK_IMAGEMODE.FSDK_IMAGE_COLOR_24BIT;
        FSDK.LoadImageFromBuffer(Image, mRGBData, mImageWidth, mImageHeight, mImageWidth*3, imagemode);
        FSDK.MirrorImage(Image, false);
        FSDK.HImage RotatedImage = new FSDK.HImage();
        FSDK.CreateEmptyImage(RotatedImage);
//        FSDK.SaveImageToFile(Image, MainActivity.getInstance().getFilesDir().getAbsolutePath()+System.currentTimeMillis()+".jpg");
        //it is necessary to work with local variables (onDraw called not the time when mImageWidth,... being reassigned, so swapping mImageWidth and mImageHeight may be not safe)
        int ImageWidth = mImageWidth;
        //int ImageHeight = mImageHeight;
        if (rotated) {
            ImageWidth = mImageHeight;
            //ImageHeight = mImageWidth;
            FSDK.RotateImage90(Image, -1, RotatedImage);
        } else {
            FSDK.CopyImage(Image, RotatedImage);
        }
        FSDK.FreeImage(Image);
        // Save first frame to gallery to debug (e.g. rotation angle)
      /*
      if (!first_frame_saved) {
         first_frame_saved = true;
         String galleryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath();
         FSDK.SaveImageToFile(RotatedImage, galleryPath + "/first_frame.jpg"); //frame is rotated!
      }
      */
        long IDs[] = new long[MAX_FACES];
        long face_count[] = new long[1];
        FSDK.FeedFrame(mTracker, 0, RotatedImage, face_count, IDs);
//        FSDK.FreeImage(RotatedImage);
        faceLock.lock();
        for (int i=0; i<MAX_FACES; ++i) {
            mFacePositions[i] = new FaceRectangle();
            mFacePositions[i].x1 = 0;
            mFacePositions[i].y1 = 0;
            mFacePositions[i].x2 = 0;
            mFacePositions[i].y2 = 0;
            mIDs[i] = IDs[i];
        }
        float ratio = (canvasWidth * 1.0f) / ImageWidth;
        for (int i = 0; i < (int)face_count[0]; ++i) {
            FSDK.FSDK_Features Eyes = new FSDK.FSDK_Features();
            FSDK.GetTrackerEyes(mTracker, 0, mIDs[i], Eyes);
            GetFaceFrame(Eyes, mFacePositions[i]);
            mFacePositions[i].x1 *= ratio;
            mFacePositions[i].y1 *= ratio;
            mFacePositions[i].x2 *= ratio;
            mFacePositions[i].y2 *= ratio;
        }
        if ((int)face_count[0] > 0) {
            FSDK.HImage img2 = new FSDK.HImage();
            FSDK.CreateEmptyImage(img2);
            FSDK.TFacePosition fp = new FSDK.TFacePosition();
            FSDK.DetectFace(RotatedImage, fp);
            int x1 = (int)(fp.xc - 1.2*fp.w/2);
            int y1 = (int)(fp.yc - 1.4*fp.w/2);
            int x2 = (int)(fp.xc + 1.2*fp.w/2);
            int y2 = (int)(fp.yc + 1.4*fp.w/2);
            FSDK.CopyRect(RotatedImage, x1, y1, x2, y2, img2);
            int maxWidth = 337;
            int maxHeight = 450;
            FSDK.ResizeImage(img2, Math.max((maxWidth+0.4)/(x2-x1+1), (maxHeight+0.4)/(y2-y1+1)), RotatedImage);
            System.out.println("face_count="+face_count[0]);
            String surveillancePhotoName = MainActivity.getInstance().getFilesDir().getAbsolutePath()+System.currentTimeMillis()+".jpg";
            FSDK.SaveImageToFile(img2, surveillancePhotoName);
            List bitmapList = new ArrayList();
            for (int i = 0; i < 1; i++) {
                NativeImg nativeImg = new NativeImg();
                nativeImg.image = FileUtil.readFile(new File(surveillancePhotoName));
                nativeImg.size = nativeImg.image.length;
                SurveillanceQueryItem surveillanceQueryItem = new SurveillanceQueryItem();
                surveillanceQueryItem.setNativeImg(nativeImg);
                //surveillanceQueryItem.setBitmap(bitmap);
                surveillanceQueryItem.setWidth("100");
                surveillanceQueryItem.setHeight("100");
                bitmapList.add(surveillanceQueryItem);
                SurveillanceMng.getInstance().addBitmap(bitmapList, nativeImg.image, 0, CommonVariables.Camera.IN+"");
            }
        }
        FSDK.FreeImage(RotatedImage);
        faceLock.unlock();
        int shift = (int)(22 * MainActivity.sDensity);
        // Mark and name faces
        for (int i=0; i<face_count[0]; ++i) {
            canvas.drawRect(mFacePositions[i].x1, mFacePositions[i].y1, mFacePositions[i].x2, mFacePositions[i].y2, mPaintBlueTransparent);
            boolean named = false;
            if (IDs[i] != -1) {
                String names[] = new String[1];
                FSDK.GetAllNames(mTracker, IDs[i], names, 1024);
                if (names[0] != null && names[0].length() > 0) {
                    canvas.drawText(names[0], (mFacePositions[i].x1+mFacePositions[i].x2)/2, mFacePositions[i].y2+shift, mPaintBlue);
                    named = true;
                }
            }
            if (!named) {
                canvas.drawText("Tap to name", (mFacePositions[i].x1+mFacePositions[i].x2)/2, mFacePositions[i].y2+shift, mPaintGreen);
            }
        }
        super.onDraw(canvas);
    } // end onDraw method
    @Override
    public boolean onTouchEvent(MotionEvent event) { //NOTE: the method can be implemented in Preview class
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                int x = (int)event.getX();
                int y = (int)event.getY();
                faceLock.lock();
                FaceRectangle rects[] = new FaceRectangle[MAX_FACES];
                long IDs[] = new long[MAX_FACES];
                for (int i=0; i<MAX_FACES; ++i) {
                    rects[i] = new FaceRectangle();
                    rects[i].x1 = mFacePositions[i].x1;
                    rects[i].y1 = mFacePositions[i].y1;
                    rects[i].x2 = mFacePositions[i].x2;
                    rects[i].y2 = mFacePositions[i].y2;
                    IDs[i] = mIDs[i];
                }
                faceLock.unlock();
                for (int i=0; i<MAX_FACES; ++i) {
                    if (rects[i] != null && rects[i].x1 <= x && x <= rects[i].x2 && rects[i].y1 <= y && y <= rects[i].y2 + 30) {
                        mTouchedID = IDs[i];
                        mTouchedIndex = i;
                        // requesting name on tapping the face
                        final EditText input = new EditText(mContext);
                        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
                        builder.setMessage("Enter person's name" )
                                .setView(input)
                                .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                                    @Override public void onClick(DialogInterface dialogInterface, int j) {
                                        FSDK.LockID(mTracker, mTouchedID);
                                        FSDK.SetName(mTracker, mTouchedID, input.getText().toString());
                                        FSDK.UnlockID(mTracker, mTouchedID);
                                        mTouchedIndex = -1;
                                    }
                                })
                                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                                    @Override public void onClick(DialogInterface dialogInterface, int j) {
                                        mTouchedIndex = -1;
                                    }
                                })
                                .setCancelable(false) // cancel with button only
                                .show();
                        break;
                    }
                }
        }
        return true;
    }
    static public void decodeYUV420SP(byte[] rgb, byte[] yuv420sp, int width, int height) {
        final int frameSize = width * height;
        int yp = 0;
        for (int j = 0; j < height; j++) {
            int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
            for (int i = 0; i < width; i++) {
                int y = (0xff & ((int) yuv420sp[yp])) - 16;
                if (y < 0) y = 0;
                if ((i & 1) == 0) {
                    v = (0xff & yuv420sp[uvp++]) - 128;
                    u = (0xff & yuv420sp[uvp++]) - 128;
                }
                int y1192 = 1192 * y;
                int r = (y1192 + 1634 * v);
                int g = (y1192 - 833 * v - 400 * u);
                int b = (y1192 + 2066 * u);
                if (r < 0) r = 0; else if (r > 262143) r = 262143;
                if (g < 0) g = 0; else if (g > 262143) g = 262143;
                if (b < 0) b = 0; else if (b > 262143) b = 262143;
                rgb[3*yp] = (byte) ((r >> 10) & 0xff);
                rgb[3*yp+1] = (byte) ((g >> 10) & 0xff);
                rgb[3*yp+2] = (byte) ((b >> 10) & 0xff);
                ++yp;
            }
        }
    }
} // end of ProcessImageAndDrawResults class
// Show video from camera and pass frames to ProcessImageAndDraw class
class Preview extends SurfaceView implements SurfaceHolder.Callback {
    Context mContext;
    SurfaceHolder mHolder;
    Camera mCamera;
    ProcessImageAndDrawResults mDraw;
    boolean mFinished;
    Preview(Context context, ProcessImageAndDrawResults draw) {
        super(context);
        mContext = context;
        mDraw = draw;
        //Install a SurfaceHolder.Callback so we get notified when the underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
    //SurfaceView callback
    public void surfaceCreated(SurfaceHolder holder) {
        mFinished = false;
        // Find the ID of the camera
        int cameraId = 0;
        boolean frontCameraFound = false;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
            Camera.getCameraInfo(i, cameraInfo);
            //if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                cameraId = i;
                frontCameraFound = true;
            }
        }
        if (frontCameraFound) {
            mCamera = Camera.open(cameraId);
        } else {
            mCamera = Camera.open();
        }
        try {
            mCamera.setPreviewDisplay(holder);
            // Preview callback used whenever new viewfinder frame is available
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                public void onPreviewFrame(byte[] data, Camera camera) {
                    if ( (mDraw == null) || mFinished )
                        return;
                    if (mDraw.mYUVData == null) {
                        // Initialize the draw-on-top companion
                        Camera.Parameters params = camera.getParameters();
                        mDraw.mImageWidth = params.getPreviewSize().width;
                        mDraw.mImageHeight = params.getPreviewSize().height;
                        mDraw.mRGBData = new byte[3 * mDraw.mImageWidth * mDraw.mImageHeight];
                        mDraw.mYUVData = new byte[data.length];
                    }
                    // Pass YUV data to draw-on-top companion
                    System.arraycopy(data, 0, mDraw.mYUVData, 0, data.length);
                    mDraw.invalidate();
                }
            });
        }
        catch (Exception exception) {
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setMessage("Cannot open camera" )
                    .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            android.os.Process.killProcess(android.os.Process.myPid());
                        }
                    })
                    .show();
            if (mCamera != null) {
                mCamera.release();
                mCamera = null;
            }
        }
    }
    //SurfaceView callback
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        // Because the CameraDevice object is not a shared resource, it's very
        // important to release it when the activity is paused.
        mFinished = true;
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
    //SurfaceView callback, configuring camera
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mCamera == null) return;
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        Camera.Parameters parameters = mCamera.getParameters();
        //Keep uncommented to work correctly on phones:
        //This is an undocumented although widely known feature
      /**/
        if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
            parameters.set("orientation", "portrait");
            mCamera.setDisplayOrientation(90); // For Android 2.2 and above
            mDraw.rotated = true;
        } else {
            parameters.set("orientation", "landscape");
            mCamera.setDisplayOrientation(0); // For Android 2.2 and above
        }
      /**/
        // choose preview size closer to 640x480 for optimal performance
        List<Camera.Size> supportedSizes = parameters.getSupportedPreviewSizes();
        int width = 0;
        int height = 0;
        for (Camera.Size s: supportedSizes) {
            if ((width - 640)*(width - 640) + (height - 480)*(height - 480) >
                    (s.width - 640)*(s.width - 640) + (s.height - 480)*(s.height - 480)) {
                width = s.width;
                height = s.height;
            }
        }
        //try to set preferred parameters
        try {
            if (width*height > 0) {
                parameters.setPreviewSize(width, height);
            }
            //parameters.setPreviewFrameRate(10);
            parameters.setSceneMode(Camera.Parameters.SCENE_MODE_PORTRAIT);
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
            mCamera.setParameters(parameters);
        } catch (Exception ex) {
        }
        mCamera.startPreview();
        parameters = mCamera.getParameters();
        Camera.Size previewSize = parameters.getPreviewSize();
        makeResizeForCameraAspect(1.0f / ((1.0f * previewSize.width) / previewSize.height));
    }
    private void makeResizeForCameraAspect(float cameraAspectRatio){
        ViewGroup.LayoutParams layoutParams = this.getLayoutParams();
        int matchParentWidth = this.getWidth();
        int newHeight = (int)(matchParentWidth/cameraAspectRatio);
        if (newHeight != layoutParams.height) {
            layoutParams.height = newHeight;
            layoutParams.width = matchParentWidth;
            this.setLayoutParams(layoutParams);
            this.invalidate();
        }
    }
} // end of Preview class