
9月3日全国放假,心情大好,虽然离过年还有段距离。
下午,客户打来电话,说以前做好的app公司(这个app是给联想做的)安全部门没过,最好加上手势密码,不过这个功能就不在加钱了,长期合作吗。我就是这样被忽悠了。心肠太好,没办法。
参看了京东钱包的做法,当用户离开app或者机子睡眠后开始定时,超过一定时间后(比如十分钟),那么,你在回到app后就显示手势密码,只有通过验证后才能进入app。
由于是每个页面都必须加手势密码,这让我很是想念j2ee中的拦截器。如果每个activity中都重复加上手势密码的代码,这显然是违反了尽量避免重复原则,另外一但有修改,维护起来将是一场灾难。最重要的一点就是,这样的代码,拿出去太丢人了。
能否集中处理那,这让我想起了kyswipeback组件,通过继承一个共有的activity实现:
完整源码:
package com.lenovo.logistics.activity.gusturelock;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.lenovo.logistics.R;
import com.lenovo.logistics.activity.BaseActivity;
import com.lenovo.logistics.activity.LoginActivity;
import com.lenovo.logistics.common.ActivityManager;
import com.lenovo.logistics.common.ConfigInfoUtil;
import com.lenovo.logistics.common.GusturelockUtil;
import com.lenovo.logistics.view.KyGustureLock;
import com.lenovo.logistics.view.KyGustureLock.OnSelectedListener;
import com.lenovo.logistics.wrapper.ConfigInfo;
import com.lenovo.logistics.wrapper.GusturelockInfo;
/**
* 实现手势密码
* @author pc
*
*/
public class GustureLockActivity extends BaseActivity{
private boolean gustureLockEnable = true;//是否关闭密码锁功能
private FrameLayout root;
private ViewGroup decorChild;
private RelativeLayout gusturelock;
/**
*这是核心原理,其他的可以不看,这个必须看
*原理:在已有的布局的外层套个FrameLayout,将手势密码布局和以前的布局都加入到这个FrameLayout中,这样
*就可以很容根据超时是否显示密码手势。
所有的Activity只要继承GustureLockActivity就可,不必单独添加代码,所有手势密码逻辑都放到GustureLockActivity即可。
**/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityManager.addActivity(this);
ViewGroup decor = (ViewGroup) this.getWindow().getDecorView();
decorChild = (ViewGroup) decor.getChildAt(0);//已有的布局,即this.setContentView(R.layout.about)设置的
decor.removeView(decorChild);
//重新生成个根布局
FrameLayout rootLayout = new FrameLayout(this);
this.root = rootLayout;
rootLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
rootLayout.addView(decorChild);
//创建手势密码布局文件
LayoutInflater inflate = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
gusturelock = (RelativeLayout)(inflate.inflate(R.layout.gusturelock, null));
gusturelock.setVisibility(View.GONE);
rootLayout.addView(gusturelock); //将其添加到新建的根布局中
//将根布局附加到窗口上
decor.addView(rootLayout);
setUpdateTime();
bindEvent();
}
@Override
protected void onResume() {
super.onResume();
decorChild.setVisibility(View.VISIBLE);
gusturelock.setVisibility(View.GONE);
if(this.gustureLockEnable && isGusturelock()){
decorChild.setVisibility(View.GONE);
gusturelock.setVisibility(View.VISIBLE);
}
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
setUpdateTime();
}
/**
* 绑定事件
*/
private void bindEvent(){
KyGustureLock kyGustureLock = (KyGustureLock)gusturelock.findViewById(R.id.kyGustureLock);
final TextView msg = (TextView)gusturelock.findViewById(R.id.msg);
//手势密码监听
kyGustureLock.setOnSelectedListener(new OnSelectedListener(){
int restNum = 5;//剩余次数
@Override
public void onSelected(View view, int[] values) {
msg.setText("");
if(restNum <= 0){
msg.setTextColor(0xFFD74A44);
msg.setText("密码错误次数过多,请重新登录");
toLoginPage("密码错误次数过多,请重新登录");
return ;
}
msg.setTextColor(0xFFFFFFFF);
if(values.length < 4){
msg.setText("至少连续绘制四个点");
return ;
}
StringBuffer sb = new StringBuffer();
for(int val : values){
if(sb.length()>0)
sb.append("_");
sb.append(val);
}
restNum--;
GusturelockInfo info = GusturelockUtil.get(GustureLockActivity.this);
if(sb.toString().equals(info.getPassword())){//登录成功
decorChild.setVisibility(View.VISIBLE);
gusturelock.setVisibility(View.GONE);
//重置
restNum = 5;
setUpdateTime();
return ;
}
if(restNum <= 0){
msg.setTextColor(0xFFD74A44);
msg.setText("密码错误次数过多,请重新登录");
toLoginPage("密码错误次数过多,请重新登录");
return ;
}
msg.setTextColor(0xFFD74A44);
msg.setText("密码错误,还可以再输入"+restNum+"次");
return ;
}
});
//忘记密码监听
TextView forgetPsw = (TextView)gusturelock.findViewById(R.id.forgetPsw);
forgetPsw.setClickable(true);
forgetPsw.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
toLoginPage("忘记手势密码,需要重新登录");
}
});
}
/**
* 设置更新时间
*/
private void setUpdateTime(){
try {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
GusturelockInfo info = GusturelockUtil.get(GustureLockActivity.this);
info.setUpdateTime(format.format(new Date()));;
GusturelockUtil.save(GustureLockActivity.this, info);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 是否需要显示手势密码
*/
private boolean isGusturelock(){
try {
GusturelockInfo info = GusturelockUtil.get(this);
if(info.getPassword() == null || info.getPassword().trim().equals(""))//未设置密码锁
return false;
if(info.getUpdateTime() == null || info.getUpdateTime().trim().equals(""))
return false;
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date updateTime = format.parse(info.getUpdateTime());
long current = System.currentTimeMillis();
if(current - updateTime.getTime() > 5 * 60 * 1000)//5分钟
//if(current - updateTime.getTime() > 5 * 1000)//5分钟
return true;
} catch (ParseException e) {
e.printStackTrace();
}
return false;
}
/**
* 手势密码是否正在显示
*/
protected boolean isGusturelockShowing(){
if(gusturelock.getVisibility() == View.VISIBLE)
return true;
return false;
}
/**
* 退出应用,同时清除密码
*/
protected void exitApplication(){
ConfigInfo info = ConfigInfoUtil.getConfigInfo(this);
info.setAutoLogin(false);
info.setPassword("");
ConfigInfoUtil.saveConfigInfo(this, info);
ActivityManager.exit();
}
/**
* 返回到登录页
*/
private void toLoginPage(String message){
//当手势密码验证没通过时,返回到登录页
}
/**
* 监听键盘
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
if(isGusturelockShowing()){
exitApplication();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
public boolean isGustureLockEnable() {
return gustureLockEnable;
}
public void setGustureLockEnable(boolean gustureLockEnable) {
this.gustureLockEnable = gustureLockEnable;
}
}KyGustureLock是手势密码控件,从组件中心http://www.see-source.com/androidwidget/list.html可以看到,
啥时候判断是否超时
可以在onResume中,这样无论是第一次进入,还是重新返回到app,或者是从睡眠状态恢复,都能立马进行处理。
@Override
protected void onResume() {
super.onResume();
decorChild.setVisibility(View.VISIBLE);
gusturelock.setVisibility(View.GONE);
if(this.gustureLockEnable && isGusturelock()){
decorChild.setVisibility(View.GONE);//隐藏正常的布局
gusturelock.setVisibility(View.VISIBLE);//显示手势密码布局
}
}离开app时开启定时
可以在onPause方法中开启定时,当用户离开app或进入睡眠状态,此时页面的焦点必定消失,onPause方法调用:
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
setUpdateTime();
}修改下记录的时间戳即可。以这个时间作为判断超时的起始时间。
大功告成,已经发给客户,目前还没反馈说有bug。

