实现一个带有指纹加密功能的笔记本(Android)第一部分

自己经常会忘记一些密码什么的,想把这些密码保存下来,但是别人做的软件总有一点不安全的感觉,所以自己动手做了一个带有指纹加密的笔记本。

以下是本工程用到的一些第三方包

compile ‘org.greenrobot:greendao:3.2.0‘
compile ‘net.zetetic:android-database-sqlcipher:3.5.1‘
compile ‘com.getbase:floatingactionbutton:1.10.1‘

其中greendao是一款比较好用的开源数据库,具体和其他开源数据库相比好在哪里我就不介绍了,百度上面会有很多分析什么的,我就简单说一下用法就好。

需要在build.gradle中添加一下内容

#位置在build.gradle的第一行
apply plugin: ‘org.greenrobot.greendao‘

#位置在android{}中,主要用于管理本地数据库版本,不同本地数据库版本之
#间升级时候需要对版本进行判断
greendao {
        schemaVersion 2
        targetGenDir ‘src/main/java‘
}

#这个是在工程的build.gradle中进行配置的
#bulidscript{
    dependencies {
        classpath ‘org.greenrobot:greendao-gradle-plugin:3.2.0‘
    }
}

以上就完成greendao的配置,可以使用greendao了。

net.zetetic:android-database-sqlcipher:3.5.1

这个是用于数据库加密的一个第三方包,可以考虑使用,也可以不使用,毕竟只要你不自己主动将本地数据库提供出去,别人就不能直接从数据库中获得相应的信息

最后一个第三方库是一个floatingActionButton,提供了floatingActionMenu,是那种可以弹出悬浮按钮的效果

下面看一下工程的结构

当然之后可能还会变化,这个是我目前的进度。

下面开始介绍项目各个部分的实现,首先是主页面,主页面是一个比较简单,除了toolbar之外只有一个RecyclerView,用来以列表的方式展示便签的简要信息。资源文件内容如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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="match_parent"
    tools:context="com.leaveme.notebook.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

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

    </android.support.design.widget.AppBarLayout>

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

    <com.getbase.floatingactionbutton.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/fab"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:fab_icon="@drawable/ic_add_white_24dp"
        />

</android.support.design.widget.CoordinatorLayout>

下面的内容就比较重要了,因为现在android版本都已经在5.0以上了,所以需要进行权限申请,需要将项目中用到的权限,比如读写存储卡的权限,指纹识别的权限,这就需要除了在Manifest中列清楚之外还要动态申请。

    private void Permissinit(){
        //需要请求的权限请求字符串列表
        List<String> permissionsNeeded = new ArrayList<String>();
        //权限请求列表
        final List<String> permissionsList = new ArrayList<String>();
        //添加读写存储空间、读取手机状态、拨打电话、读取位置信息、读取精确位置信息这些权限到权限请求列表中
        if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))
            permissionsNeeded.add("\n\r读存储空间");
        if (!addPermission(permissionsList, Manifest.permission.READ_EXTERNAL_STORAGE))
            permissionsNeeded.add("\n\r写存储空间");
        if (!addPermission(permissionsList, Manifest.permission.INTERNET))
            permissionsNeeded.add("\n\r联网");
        if (!addPermission(permissionsList, Manifest.permission.USE_FINGERPRINT))
            permissionsNeeded.add("\n\r使用指纹识别");
        //如果权限请求列表中的内容大于0个,则开始请求权限
        if (permissionsList.size() > 0) {
            if (permissionsNeeded.size() > 0) {
                //获取到第一个需要添加请求列表的权限
                String message = "你需要获取已下权限:" + permissionsNeeded.get(0);
                //循环将剩余需要请求的权限加入到请求列表
                for (int i = 1; i < permissionsNeeded.size(); i++)
                    message = message + ", " + permissionsNeeded.get(i);
                showMessageOKCancel(message,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                ActivityCompat.requestPermissions(MainActivity.this,permissionsList.toArray(new String[permissionsList.size()]),
                                        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                            }
                        });
                return;
            }
            //开始向系统请求权限
            ActivityCompat.requestPermissions(this,permissionsList.toArray(new String[permissionsList.size()]),
                    REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
        }
    }

先将需要请求的权限打包放在权限求情列表中,当然首先判断是否需要请求该权限,毕竟有的权限不需要动态请求也是可以获得的,这个时候就需要

    private boolean addPermission(List<String> permissionsList, String permission) {
        //判断该应用是否具备要请求的权限
        if (ContextCompat.checkSelfPermission(this,permission) != PackageManager.PERMISSION_GRANTED) {
            //没有该权限,则加入到请求列表
            permissionsList.add(permission);
            if (!ActivityCompat.shouldShowRequestPermissionRationale(this,permission))
                return false;
        }
        return true;
    }

checkSelfPermission就是判断该APP是否可以使用某种权限。当所有需要的权限都加入到权限请求列表后,并且该列表的大小不是0个,就可以向用户请求者写权限了。

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(MainActivity.this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

这个UI对话框的UI界面可以简单地向用户描述为什么需要这些权限。

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode){
            //判断是否为该软件系统请求的权限信息
            case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
                //判断是否请求成功
            {
                //请求成功
                if(grantResults.length>0&&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                }
                else{
//                    //请求失败,提示用户“请求权限失败
                    Log.e("TAG","请求权限失败");
//                    Toast.makeText(this,"请求权限失败,请手动设置",Toast.LENGTH_LONG).show();
//                    this.finish();
                }
            }break;
        }
    }

当请求获得反馈信息后,会执行onRequestPermissionsResult函数,然后可以根据是否请求成功进行相应的操作。

这样就完成了一整套动态权限获取的流程。

然后是列表页面的实现,采用RecyclerView的主要原因是其在实现了listview功能的基础上,加入了更加规范的viewHolder,而且item的复用工作不需要手动管理。前期的实现代码如下,在创建的时候初始化加载数据,然后通过onbindViewHolder将数据绑定到每一个item上,逻辑实现比较清晰。资源文件比较简单,不单独列出了,在文章的末尾提供了整个项目的github地址,方便下载查看

public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ItemViewHolder> implements ItemTouchHelperAdapter{

    private List<Note> notes = new ArrayList<>();
    private Context context;
    private DaoSession session;
    private NoteDao noteDao;

    private final OnStartDragListener mDragStartListener;

    public NoteAdapter(Context context, OnStartDragListener dragStartListener){
        mDragStartListener = dragStartListener;
        this.context = context;
        init();
    }

    private void init(){
        session = GreenDaoHelper.getDaoSession(context);
        noteDao= session.getNoteDao();
        notes = noteDao.loadAll();
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
        ItemViewHolder itemViewHolder = new ItemViewHolder(view);
        return itemViewHolder;
    }

    @Override
    public void onBindViewHolder(ItemViewHolder holder, final int position) {
        RecyclerView.ViewHolder viewHolder = (RecyclerView.ViewHolder)holder;
        ViewGroup.LayoutParams layoutParams = viewHolder.itemView.getLayoutParams();
        layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;

        holder.content.setText(notes.get(position).getContent());
        holder.time.setText(dateFormatString.transform(notes.get(position).getTimeStamp()));
        holder.title.setText(notes.get(position).getTitle());

        ((RecyclerView.ViewHolder) holder).itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClass(context, NoteActivity.class);
                intent.setAction(notes.get(position).getTimeStamp()+"");
                context.startActivity(intent);
            }
        });
    }

    @Override
    public int getItemCount() {
        if(noteDao.count()!=notes.size()){
            notes = noteDao.loadAll();
            return notes.size();
        }
        return notes.size();
    }

    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        Collections.swap(notes, fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    @Override
    public void onItemDismiss(final int position) {
        new CommomDialog(context, R.style.dialog, "您确定删除此条记录?", new CommomDialog.OnCloseListener() {
            @Override
            public void onClick(Dialog dialog, boolean confirm) {
                if(confirm){
                    session.getNoteDao().delete(notes.get(position));
                    notes.remove(position);
                    notifyItemRemoved(position);
                    dialog.dismiss();
                }

            }
        }).setTitle("提示").show();
    }

    public static class ItemViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder{

        public final TextView title;
        public final TextView time;
        public final TextView content;
        public ItemViewHolder(View itemView) {
            super(itemView);
            title = (TextView)itemView.findViewById(R.id.tv_item_tile);
            time = (TextView)itemView.findViewById(R.id.tv_item_time);
            content = (TextView)itemView.findViewById(R.id.tv_item_content);
        }

        @Override
        public void onItemSelected() {
            itemView.setBackgroundColor(Color.LTGRAY);
        }

        @Override
        public void onItemClear() {
            itemView.setBackgroundColor(0);
        }
    }
}

在这里用到了greendao数据库初始化加载数据,具体的greendao的使用,我这里不介绍了,网上有很多教程可以找到,例如:Android GreenDao使用教程。列一下我的数据库格式,包括了以下这些column。其中@entity关键字表示这是一个实体Bean,会被greendao自动编译成一个数据库表,并形成一个notedao来管理数据库的增删查改等操作。

@Entity
public class Note {
    @Id(autoincrement = true)
    private long id;

    private String title;//笔记标题
    private String content;//笔记内容
    private long timeStamp;//笔记时间
    private int state;//笔记状态  0:正常状态 1:加密状态 -1:已删除状态
    private String pictureId;//笔记中的图片id
    private long index;//显示顺序排序
    @Generated(hash = 587745031)
    public Note(long id, String title, String content, long timeStamp, int state,
            String pictureId, long index) {
        this.id = id;
        this.title = title;
        this.content = content;
        this.timeStamp = timeStamp;
        this.state = state;
        this.pictureId = pictureId;
        this.index = index;
    }
    @Generated(hash = 1272611929)
    public Note() {
    }
    public long getId() {
        return this.id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getTitle() {
        return this.title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return this.content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public long getTimeStamp() {
        return this.timeStamp;
    }
    public void setTimeStamp(long timeStamp) {
        this.timeStamp = timeStamp;
    }
    public int getState() {
        return this.state;
    }
    public void setState(int state) {
        this.state = state;
    }
    public String getPictureId() {
        return this.pictureId;
    }
    public void setPictureId(String pictureId) {
        this.pictureId = pictureId;
    }
    public long getIndex() {
        return this.index;
    }
    public void setIndex(long index) {
        this.index = index;
    }

}

关于greendao自身的管理,即保证项目中仅有一个数据库,则需要创建一个greendaohelper继承自application,然后将Daomaster和DaoSession设计为单例模式。我参考了这一篇文章,Android GreenDao使用总结(包括模型生成、增删改查、修改存储路径、数据库更新升级和加解密数据库)

最后是NoteActivity的实现,就是写一些笔记的Activity。实现起来非常容易,页面上只有两个edittext,对其进行简单的配置即可。值得注意的是需要对启动来源进行判断,是来自直接新增一个条目,还是要修改一个条目,这里我采用的是传入一个timestamp参数,如果这个参数为空则表示是一个新增条目,则新建一个笔记条目。如果有这个timestamp参数,且不为空,那么就从数据库中获取该条笔记,并加载其中的内容,以备修改和查看。保存则是在推出的时候自动保存,包括点击android系统返回键退出或者点击上面的返回图表退出。public class NoteActivity extends AppCompatActivity {    private EditText title;

private EditText content;

    private Note note;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_note);

        DaoSession session = GreenDaoHelper.getDaoSession(this);
        NoteDao noteDao= session.getNoteDao();

        Intent intent = getIntent();
        String time = intent.getAction();
        long currentTime = 0;     //判断启动来源,决定是否需要新建一条笔记
        if(null==time||time.equals("")){       //需要新建一条笔记
            currentTime = new Date().getTime();
            note = new Note();
            note.setId(noteDao.count());
            note.setContent("");
            note.setTitle("");
            note.setPictureId("");
            note.setState(0);
            note.setTimeStamp(currentTime);
        }else {       //获取数据库中已有笔记
            currentTime = Long.parseLong(time.trim());
            List<Note> n = noteDao.queryBuilder().where(NoteDao.Properties.TimeStamp.eq(currentTime)).list();
            if(n.size()>0) {
                note = n.get(0);
            }else {
                Toast.makeText(this,"something error",Toast.LENGTH_SHORT).show();
            }
        }

        initView();
    }

    private void initView(){
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                storeNote();
                NoteActivity.this.finish();
            }
        });
        title = (EditText) findViewById(R.id.edt_title);
        content = (EditText)findViewById(R.id.edt_content);

title.setText(note.getTitle());
        content.setText(note.getContent());
    }

    private void storeNote(){
        DaoSession session = GreenDaoHelper.getDaoSession(this);
        NoteDao noteDao= session.getNoteDao();
        note.setContent(content.getText().toString());
        note.setTitle(title.getText().toString());     //插入或者替换笔记
        noteDao.insertOrReplace(note);
    }

    @Override
    protected void onPause() {
        super.onPause();           //在这里写保存笔记的目的是为了防止因为activity的生命周期执行到onstop的时候,主界面的列表已经刷新了,进而导致数据同步延迟
        storeNote();
    }
}

这样基本上算是实现了一个笔记本的基本功能,然后指纹加密部分,以及实现功能的时候遇到的一些小问题写在下半部分。附上本项目代码:一个指纹加密的笔记本

原文地址:https://www.cnblogs.com/leaveMeAlone/p/8867568.html

时间: 2024-10-28 20:58:55

实现一个带有指纹加密功能的笔记本(Android)第一部分的相关文章

实现一个带有指纹加密功能的笔记本(Android)第二部分

上文基本完成了整个笔记本的笔记功能的实现,接下来记录实现指纹识别加密以及一些小注意事项. 首先判断该手机是否具备指纹识别的硬件功能和用户是否开启指纹识别. public boolean isFinger() { //用来检查是否有指纹识别权限 if(checkCallingOrSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED) { if (!manager.isHard

分享一个带有合计行功能的DataGridView扩展

因为一个Winform的项目中需要用到带有合计行的表格,并且需要满足以下需求: 合计行可自动对需要求和的列进行求和计算; 合计行必须固定(冻结)在表格的最底部,且其位置不受滚动条的滚动而移动; 可以设置合计行单元格的数据展示格式. 本以为winform程序出来已经这么多年了,这个本也是个比较基础的功能,网上应该有很多现成的例子,便想着直接从网上找个例子用到项目中即可,无奈找了很久也没有见一个合适的实现,迫于无奈,只能动手自己写了一个DataGridView的扩展.并在这里整理出来,以分享给后续有

在存放源程序的文件夹中建立一个子文件夹 myPackage。例如,在“D:\java”文件夹之中创建一个与包同名的子文件夹 myPackage(D:\java\myPackage)。在 myPackage 包中创建一个YMD类,该类具有计算今年的年份、可以输出一个带有年月日的字符串的功能。设计程序SY31.java,给定某人姓名和出生日期,计算该人年龄,并输出该人姓名、年龄、出生日期。程序使用YM

题目补充: 在存放源程序的文件夹中建立一个子文件夹 myPackage.例如,在"D:\java"文件夹之中创建一个与包同名的子文件夹 myPackage(D:\java\myPackage).在 myPackage 包中创建一个YMD类,该类具有计算今年的年份.可以输出一个带有年月日的字符串的功能.设计程序SY31.java,给定某人姓名和出生日期,计算该人年龄,并输出该人姓名.年龄.出生日期.程序使用YMD的方法来计算年龄. 主要考包的运用 用到java.util.Calendar

Kitkat的加密功能对应用做了什么?

本文只分析手机加密后,启动到输入密码的界面的流程. 一. 加密后,系统服务针对加密功能做了什么? 最先启动的是SystemServer,调用ServerThread的initAndLoop()方法,开始启动系统的其他的服务. 在该文件中搜索"crypt",得到如下内容: 1. private static final String ENCRYPTING_STATE = "trigger_restart_min_framework"; private static f

给 Qt sqlite 增加加密功能

整合sqlite代码 开源的sqlite中没有实现加密的功能,所以如果需要加密功能,需要自己实现 sqlite3_keysqlite3_rekey 等相关函数 不过开源的 wxsqlite3中已经实现了加密,所以只要将这里的代码整合到qt 中就行,主要是将其实现的 sqlite3_key sqlite3_rekey 等函数添加到 qt 的 sqlite3.c 中 这里有一份已经整合好的代码qt_sqlite_driver.zip 直接解压到 qtbase\src\3rdparty 下就行 整合主

设置-安全-手机加密功能讲解

        此功能可以用来加密手机上的所有数据,包括 Google 帐户.应用数据.音乐和其他媒体信息.已下载的信息等.如果执行了加密操作,您每次开机时都必须输入数字 PIN 或密码. 请注意,上述 PIN 或密码与您在未加密状态下解锁手机时所用的相同,无法单独设置. 警告:加密操作无法撤消.要将手机恢复为未加密状态,唯一的方法是恢复出厂设置,但这会清除您的所有数据. 如果您的手机遭窃,加密可提供额外保护,某些组织可能要求或建议使用加密.在启用加密前,请先咨询您的系统管理员.很多情况下,您设

用struts2做一个带有图片效果的登陆验证码

我们在登陆网站的时候总是会有带有图片验证功能的验证码,它是怎么做出来的了,今天我就详细的将每一步步骤写出来. 1.在action层 1 package cn.itcast.javaee.js.checkcode; 2 3 import java.io.PrintWriter; 4 import javax.servlet.http.HttpServletResponse; 5 import org.apache.struts2.ServletActionContext; 6 import com

BitLocker加密功能

BitLocker加密功能 u 案例需求 怎样使用Windows 7的BitLocker功能加密磁盘驱动器,保护文件数据? u 知识提示 微软在Windows 7操作系统旗舰版中为用户提供了一种强悍的BitLocker加密功能,该技术最早出现在Vista系统中,BitLocker机密技术能够同时支持FAT和NTFS两种格式,用来加密保护用户数据,可以加密电脑的整个系统分区,页可以加密可移动的便携存储设备,如U盘和移动硬盘等.其中对U盘等移动设备存储进行加密的BitLocker TO GO就是最新

添加指纹识别功能

iPhone 5S开始硬件支持指纹识别功能,在iOS 8以后支持指纹识别,今天随意试了试指纹识别功能的识别,写了一个小demo 1.环境 引入头文件 #import <LocalAuthentication/LocalAuthentication.h> 2.创建两个页面 第一个页面添加一个button,用来触发指纹识别的功能 第二个页面就加个label// // ViewController.m // FingerPrint // // Created by Silence on 16/1/2