增加密码锁功能 #9

Merged
pr9ixgmc2 merged 1 commits from cuijiaxiang_branch into master 1 month ago

@ -127,12 +127,32 @@
<activity
android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.Light" >
</activity>
<activity
android:name=".ui.SetLockActivity"
android:label="@string/lock_pattern_title"
android:theme="@style/NoteTheme"
android:exported="true">
</activity>
<activity
android:name=".ui.UnlockActivity"
android:label="@string/lock_note_title"
android:theme="@style/NoteTheme"
android:exported="true">
</activity>
<activity
android:name=".ui.NumericPasswordActivity"
android:label="@string/numeric_password_title"
android:theme="@style/NoteTheme"
android:exported="true">
</activity>
<service
android:name="net.micode.notes.gtask.remote.GTaskSyncService"
android:exported="false" >

@ -223,6 +223,30 @@ public class Notes {
* <P> : INTEGER (long) </P>
*/
public static final String PIN_PRIORITY = "pin_priority";
/**
* 便
* <P> : INTEGER (0: , 1: ) </P>
*/
public static final String IS_LOCKED = "is_locked";
/**
*
* <P> : TEXT </P>
*/
public static final String LOCK_PASSWORD = "lock_password";
/**
* 便使
* <P> : TEXT ("gesture" "numeric") </P>
*/
public static final String PASSWORD_TYPE = "password_type";
/**
* 6
* <P> : TEXT </P>
*/
public static final String NUMERIC_PASSWORD = "numeric_password";
}
/**

@ -36,7 +36,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "note.db";
// 数据库版本号
private static final int DB_VERSION = 5;
private static final int DB_VERSION = 7;
// 数据库表名定义
public interface TABLE {
@ -92,7 +92,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.IS_PINNED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.PIN_PRIORITY + " INTEGER NOT NULL DEFAULT 0" +
NoteColumns.PIN_PRIORITY + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.IS_LOCKED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCK_PASSWORD + " TEXT NOT NULL DEFAULT ''" +
")";
// 创建数据表的SQL语句
@ -420,7 +422,16 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
oldVersion++;
}
// 如果需要,重新创建触发器
if (oldVersion == 5) {
upgradeToV6(db);
oldVersion++;
}
if (oldVersion == 6) {
upgradeToV7(db);
oldVersion++;
}
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db);
@ -491,4 +502,32 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.PIN_PRIORITY
+ " INTEGER NOT NULL DEFAULT 0");
}
/**
* v5v6
* 便便
* @param db SQLite
*/
private void upgradeToV6(SQLiteDatabase db) {
// 为笔记表添加锁定状态字段
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.IS_LOCKED
+ " INTEGER NOT NULL DEFAULT 0");
// 为笔记表添加密码字段
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_PASSWORD
+ " TEXT NOT NULL DEFAULT ''");
}
/**
* v6v7
*
* @param db SQLite
*/
private void upgradeToV7(SQLiteDatabase db) {
// 为笔记表添加密码类型字段
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.PASSWORD_TYPE
+ " TEXT NOT NULL DEFAULT 'gesture'");
// 为笔记表添加数字密码字段
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.NUMERIC_PASSWORD
+ " TEXT NOT NULL DEFAULT ''");
}
}

@ -108,6 +108,26 @@ public class WorkingNote {
*/
private NoteSettingChangedListener mNoteSettingStatusListener;
/**
* 便便
*/
private boolean mIsLocked;
/**
* 便
*/
private String mLockPassword;
/**
* 便使
*/
private String mPasswordType;
/**
* 6
*/
private String mNumericPassword;
/**
* 便
*/
@ -130,7 +150,11 @@ public class WorkingNote {
NoteColumns.BG_COLOR_ID, // 背景颜色ID
NoteColumns.WIDGET_ID, // 小部件ID
NoteColumns.WIDGET_TYPE, // 小部件类型
NoteColumns.MODIFIED_DATE // 修改日期
NoteColumns.MODIFIED_DATE, // 修改日期
NoteColumns.IS_LOCKED, // 锁定状态
NoteColumns.LOCK_PASSWORD, // 锁定密码
NoteColumns.PASSWORD_TYPE, // 密码类型
NoteColumns.NUMERIC_PASSWORD // 数字密码
};
/**
@ -150,6 +174,10 @@ public class WorkingNote {
private static final int NOTE_WIDGET_ID_COLUMN = 3; // 小部件ID列索引
private static final int NOTE_WIDGET_TYPE_COLUMN = 4; // 小部件类型列索引
private static final int NOTE_MODIFIED_DATE_COLUMN = 5; // 修改日期列索引
private static final int NOTE_IS_LOCKED_COLUMN = 6; // 锁定状态列索引
private static final int NOTE_LOCK_PASSWORD_COLUMN = 7; // 锁定密码列索引
private static final int NOTE_PASSWORD_TYPE_COLUMN = 8; // 密码类型列索引
private static final int NOTE_NUMERIC_PASSWORD_COLUMN = 9; // 数字密码列索引
/**
* 便
@ -202,6 +230,10 @@ public class WorkingNote {
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
mIsLocked = cursor.getInt(NOTE_IS_LOCKED_COLUMN) > 0;
mLockPassword = cursor.getString(NOTE_LOCK_PASSWORD_COLUMN);
mPasswordType = cursor.getString(NOTE_PASSWORD_TYPE_COLUMN);
mNumericPassword = cursor.getString(NOTE_NUMERIC_PASSWORD_COLUMN);
}
cursor.close();
} else {
@ -537,6 +569,82 @@ public class WorkingNote {
return mWidgetType;
}
/**
* 便
* @param locked
*/
public void setLocked(boolean locked) {
if (mIsLocked != locked) {
mIsLocked = locked;
mNote.setNoteValue(NoteColumns.IS_LOCKED, String.valueOf(locked ? 1 : 0));
}
}
/**
* 便
* @return
*/
public boolean isLocked() {
return mIsLocked;
}
/**
*
* @param password
*/
public void setLockPassword(String password) {
if (!TextUtils.equals(mLockPassword, password)) {
mLockPassword = password;
mNote.setNoteValue(NoteColumns.LOCK_PASSWORD, password);
}
}
/**
*
* @return
*/
public String getLockPassword() {
return mLockPassword;
}
/**
*
* @param type ("gesture" "numeric")
*/
public void setPasswordType(String type) {
if (!TextUtils.equals(mPasswordType, type)) {
mPasswordType = type;
mNote.setNoteValue(NoteColumns.PASSWORD_TYPE, type);
}
}
/**
*
* @return
*/
public String getPasswordType() {
return mPasswordType;
}
/**
*
* @param password
*/
public void setNumericPassword(String password) {
if (!TextUtils.equals(mNumericPassword, password)) {
mNumericPassword = password;
mNote.setNoteValue(NoteColumns.NUMERIC_PASSWORD, password);
}
}
/**
*
* @return
*/
public String getNumericPassword() {
return mNumericPassword;
}
/**
* 便
* 便

@ -0,0 +1,158 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.tool;
import android.text.TextUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*
*
*/
public class LockPasswordUtils {
private static final String TAG = "LockPasswordUtils";
private static final String SALT = "micode_notes_lock_salt";
/**
*
*/
public static final String TYPE_GESTURE = "gesture";
/**
*
*/
public static final String TYPE_NUMERIC = "numeric";
/**
*
* @param password
* @return
*/
public static String encryptPassword(String password) {
if (TextUtils.isEmpty(password)) {
return "";
}
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
String saltedPassword = password + SALT;
byte[] hash = md.digest(saltedPassword.getBytes());
return bytesToHex(hash);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
}
/**
*
* @param password
* @param encryptedPassword
* @return
*/
public static boolean verifyPassword(String password, String encryptedPassword) {
if (TextUtils.isEmpty(password) || TextUtils.isEmpty(encryptedPassword)) {
return false;
}
String encrypted = encryptPassword(password);
return encrypted.equals(encryptedPassword);
}
/**
*
* @param bytes
* @return
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
/**
*
* @param pattern
* @return
*/
public static String patternToString(int[] pattern) {
if (pattern == null || pattern.length == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pattern.length; i++) {
sb.append(pattern[i]);
if (i < pattern.length - 1) {
sb.append(",");
}
}
return sb.toString();
}
/**
*
* @param patternString
* @return
*/
public static int[] stringToPattern(String patternString) {
if (TextUtils.isEmpty(patternString)) {
return new int[0];
}
String[] parts = patternString.split(",");
int[] pattern = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
try {
pattern[i] = Integer.parseInt(parts[i].trim());
} catch (NumberFormatException e) {
pattern[i] = 0;
}
}
return pattern;
}
/**
*
* @param pattern
* @return 4
*/
public static boolean isValidPattern(int[] pattern) {
return pattern != null && pattern.length >= 4;
}
/**
*
* @param password
* @return 6
*/
public static boolean isValidNumericPassword(String password) {
if (TextUtils.isEmpty(password)) {
return false;
}
if (password.length() != 6) {
return false;
}
for (char c : password.toCharArray()) {
if (!Character.isDigit(c)) {
return false;
}
}
return true;
}
}

@ -0,0 +1,266 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import net.micode.notes.R;
import net.micode.notes.tool.LockPasswordUtils;
import java.util.ArrayList;
import java.util.List;
/**
*
* 3x3
*/
public class LockPatternView extends View {
private static final String TAG = "LockPatternView";
private static final int GRID_SIZE = 3;
private static final int DOT_RADIUS = 10;
private static final int LINE_WIDTH = 8;
private Paint mDotPaint;
private Paint mLinePaint;
private Paint mSelectedDotPaint;
private float[] mDotPositions;
private List<Integer> mPattern;
private Path mPath;
private boolean mIsDrawing;
private float mLastX;
private float mLastY;
private OnPatternListener mListener;
/**
*
*/
public interface OnPatternListener {
void onPatternStart();
void onPatternCleared();
void onPatternDetected(List<Integer> pattern);
}
public LockPatternView(Context context) {
super(context);
init();
}
public LockPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LockPatternView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mDotPaint = new Paint();
mDotPaint.setColor(getResources().getColor(R.color.secondary_text_dark));
mDotPaint.setAntiAlias(true);
mDotPaint.setStyle(Paint.Style.FILL);
mSelectedDotPaint = new Paint();
mSelectedDotPaint.setColor(getResources().getColor(android.R.color.holo_blue_dark));
mSelectedDotPaint.setAntiAlias(true);
mSelectedDotPaint.setStyle(Paint.Style.FILL);
mLinePaint = new Paint();
mLinePaint.setColor(getResources().getColor(android.R.color.holo_blue_dark));
mLinePaint.setAntiAlias(true);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setStrokeWidth(LINE_WIDTH);
mLinePaint.setStrokeCap(Paint.Cap.ROUND);
mPattern = new ArrayList<>();
mPath = new Path();
mDotPositions = new float[GRID_SIZE * GRID_SIZE * 2];
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
calculateDotPositions(w, h);
}
private void calculateDotPositions(int width, int height) {
int padding = Math.min(width, height) / 10;
int availableWidth = width - 2 * padding;
int availableHeight = height - 2 * padding;
int cellWidth = availableWidth / (GRID_SIZE - 1);
int cellHeight = availableHeight / (GRID_SIZE - 1);
for (int row = 0; row < GRID_SIZE; row++) {
for (int col = 0; col < GRID_SIZE; col++) {
int index = (row * GRID_SIZE + col) * 2;
mDotPositions[index] = padding + col * cellWidth;
mDotPositions[index + 1] = padding + row * cellHeight;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawDots(canvas);
drawPattern(canvas);
}
private void drawDots(Canvas canvas) {
for (int row = 0; row < GRID_SIZE; row++) {
for (int col = 0; col < GRID_SIZE; col++) {
int dotIndex = row * GRID_SIZE + col;
int index = dotIndex * 2;
float x = mDotPositions[index];
float y = mDotPositions[index + 1];
if (mPattern.contains(dotIndex)) {
canvas.drawCircle(x, y, DOT_RADIUS + 2, mSelectedDotPaint);
} else {
canvas.drawCircle(x, y, DOT_RADIUS, mDotPaint);
}
}
}
}
private void drawPattern(Canvas canvas) {
if (mPattern.isEmpty()) {
return;
}
mPath.reset();
int firstIndex = mPattern.get(0);
int firstIndex2 = firstIndex * 2;
mPath.moveTo(mDotPositions[firstIndex2], mDotPositions[firstIndex2 + 1]);
for (int i = 1; i < mPattern.size(); i++) {
int index = mPattern.get(i);
int index2 = index * 2;
mPath.lineTo(mDotPositions[index2], mDotPositions[index2 + 1]);
}
if (mIsDrawing) {
mPath.lineTo(mLastX, mLastY);
}
canvas.drawPath(mPath, mLinePaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mIsDrawing = true;
mPattern.clear();
mPath.reset();
mLastX = x;
mLastY = y;
checkDot(x, y);
if (mListener != null) {
mListener.onPatternStart();
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsDrawing) {
mLastX = x;
mLastY = y;
checkDot(x, y);
}
break;
case MotionEvent.ACTION_UP:
if (mIsDrawing) {
mIsDrawing = false;
if (mListener != null) {
mListener.onPatternDetected(new ArrayList<>(mPattern));
}
}
break;
default:
break;
}
invalidate();
return true;
}
private void checkDot(float x, float y) {
for (int row = 0; row < GRID_SIZE; row++) {
for (int col = 0; col < GRID_SIZE; col++) {
int dotIndex = row * GRID_SIZE + col;
int index2 = dotIndex * 2;
float dotX = mDotPositions[index2];
float dotY = mDotPositions[index2 + 1];
float distance = (float) Math.sqrt(Math.pow(x - dotX, 2) + Math.pow(y - dotY, 2));
if (distance < DOT_RADIUS * 3 && !mPattern.contains(dotIndex)) {
mPattern.add(dotIndex);
break;
}
}
}
}
public void clearPattern() {
mPattern.clear();
mPath.reset();
mIsDrawing = false;
invalidate();
if (mListener != null) {
mListener.onPatternCleared();
}
}
public void setOnPatternListener(OnPatternListener listener) {
mListener = listener;
}
public List<Integer> getPattern() {
return new ArrayList<>(mPattern);
}
public boolean isPatternValid() {
return LockPasswordUtils.isValidPattern(convertPatternToIntArray());
}
private int[] convertPatternToIntArray() {
int[] pattern = new int[mPattern.size()];
for (int i = 0; i < mPattern.size(); i++) {
pattern[i] = mPattern.get(i);
}
return pattern;
}
}

@ -58,6 +58,7 @@ import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.LockPasswordUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
@ -78,7 +79,7 @@ import java.util.regex.Pattern;
* 便 checklist
*
* </p>
*
*
* @author MiCode Open Source Community
* @version 1.0
*/
@ -170,7 +171,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
*
* </p>
*
*
* @param savedInstanceState
*/
@Override
@ -192,7 +193,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
*
* </p>
*
*
* @param savedInstanceState
*/
@Override
@ -215,13 +216,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
* 便便
* </p>
*
*
* @param intent
* @return
*/
private boolean initActivityState(Intent intent) {
mWorkingNote = null;
// 处理查看便签操作
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
@ -305,7 +306,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
finish();
return false;
}
// 设置设置状态变化监听器
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
@ -333,7 +334,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
// 设置编辑器字体大小
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
// 根据便签模式显示内容
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 切换到列表模式
@ -344,12 +345,12 @@ public class NoteEditActivity extends Activity implements OnClickListener,
// 将光标定位到文本末尾
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
// 隐藏所有背景选择状态
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
// 设置头部和编辑器背景
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
@ -396,7 +397,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
*
* </p>
*
*
* @param intent
*/
@Override
@ -410,18 +411,18 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
* 便
* </p>
*
*
* @param outState Bundle
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 对于新便签先保存生成ID
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
// 保存便签ID
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
@ -432,7 +433,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
*
* </p>
*
*
* @param ev
* @return
*/
@ -451,14 +452,14 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
// 其他情况交给父类处理
return super.dispatchTouchEvent(ev);
}
/**
*
*
*
* @param view
* @param ev
* @return
@ -468,14 +469,14 @@ public class NoteEditActivity extends Activity implements OnClickListener,
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
// 检查触摸坐标是否在视图范围内
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {
return false;
}
return false;
}
return true;
}
@ -494,11 +495,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
// 初始化编辑器
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
// 初始化背景颜色选择器
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
for (int id : sBgSelectorBtnsMap.keySet()) {
@ -512,16 +513,16 @@ public class NoteEditActivity extends Activity implements OnClickListener,
View view = findViewById(id);
view.setOnClickListener(this);
};
// 初始化共享偏好设置和字体大小
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
// 修复字体大小ID可能超出范围的问题
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
// 初始化编辑列表
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
@ -559,7 +560,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
mWorkingNote.getWidgetId()
});
sendBroadcast(intent);
@ -571,7 +572,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
* UI
* </p>
*
*
* @param v
*/
public void onClick(View v) {
@ -622,7 +623,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
*
* </p>
*
*
* @return
*/
private boolean clearSettingState() {
@ -654,7 +655,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
* 便
* </p>
*
*
* @param menu
* @return
*/
@ -663,10 +664,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (isFinishing()) {
return true;
}
// 清除设置状态
clearSettingState();
// 根据便签类型加载不同菜单
menu.clear();
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
@ -674,20 +675,23 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
// 根据便签模式调整列表模式菜单项标题
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
} else {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
}
// 根据是否有提醒调整提醒相关菜单项可见性
if (mWorkingNote.hasClockAlert()) {
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
menu.findItem(R.id.menu_set_password).setVisible(!mWorkingNote.isLocked());
menu.findItem(R.id.menu_remove_lock).setVisible(mWorkingNote.isLocked());
return true;
}
@ -696,7 +700,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
* 便
* </p>
*
*
* @param item
* @return
*/
@ -735,6 +739,23 @@ public class NoteEditActivity extends Activity implements OnClickListener,
setReminder();
} else if (id == R.id.menu_delete_remind) {
mWorkingNote.setAlertDate(0, false);
} else if (id == R.id.menu_set_password) {
showPasswordTypeDialog();
} else if (id == R.id.menu_remove_lock) {
Intent intent;
String passwordType = mWorkingNote.getPasswordType();
if (LockPasswordUtils.TYPE_GESTURE.equals(passwordType)) {
intent = new Intent(this, SetLockActivity.class);
intent.putExtra(SetLockActivity.EXTRA_NOTE_ID, mWorkingNote.getNoteId());
intent.putExtra(SetLockActivity.EXTRA_MODE, SetLockActivity.MODE_REMOVE);
} else if (LockPasswordUtils.TYPE_NUMERIC.equals(passwordType)) {
intent = new Intent(this, NumericPasswordActivity.class);
intent.putExtra(NumericPasswordActivity.EXTRA_NOTE_ID, mWorkingNote.getNoteId());
intent.putExtra(NumericPasswordActivity.EXTRA_MODE, NumericPasswordActivity.MODE_REMOVE);
} else {
return true;
}
startActivity(intent);
} else {
// 默认情况,什么也不做
}
@ -748,13 +769,39 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* </p>
*/
private void setReminder() {
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date , true);
}
});
d.show();
showPasswordTypeDialog();
}
/**
*
* <p>
*
* </p>
*/
private void showPasswordTypeDialog() {
new android.app.AlertDialog.Builder(this)
.setTitle(R.string.dialog_set_password_title)
.setItems(new String[] {
getString(R.string.dialog_set_gesture_password),
getString(R.string.dialog_set_numeric_password)
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
Intent intent = new Intent(NoteEditActivity.this, SetLockActivity.class);
intent.putExtra(SetLockActivity.EXTRA_NOTE_ID, mWorkingNote.getNoteId());
intent.putExtra(SetLockActivity.EXTRA_MODE, SetLockActivity.MODE_SET);
startActivity(intent);
} else if (which == 1) {
Intent intent = new Intent(NoteEditActivity.this, NumericPasswordActivity.class);
intent.putExtra(NumericPasswordActivity.EXTRA_NOTE_ID, mWorkingNote.getNoteId());
intent.putExtra(NumericPasswordActivity.EXTRA_MODE, NumericPasswordActivity.MODE_SET);
startActivity(intent);
}
}
})
.setCancelable(true)
.show();
}
/**
@ -762,7 +809,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
* 便ACTION_SENDtext/plain
* </p>
*
*
* @param context
* @param info
*/
@ -806,7 +853,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} else {
Log.d(TAG, "Wrong note id, should not happen");
}
// 根据同步模式处理删除
if (!isSyncMode()) {
// 非同步模式,直接删除
@ -820,7 +867,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
}
}
// 标记为已删除
mWorkingNote.markDeleted(true);
}
@ -830,7 +877,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
*
* </p>
*
*
* @return
*/
private boolean isSyncMode() {
@ -842,7 +889,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* <p>
*
* </p>
*
*
* @param date
* @param set
*/
@ -851,17 +898,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
// 创建闹钟意图
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
// 更新提醒头部
showAlertHeader();
// 设置或取消闹钟
if(!set) {
alarmManager.cancel(pendingIntent);

@ -48,6 +48,7 @@ public class NoteItemData {
NoteColumns.TYPE,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.IS_LOCKED,
};
/**
@ -106,6 +107,10 @@ public class NoteItemData {
*
*/
private static final int WIDGET_TYPE_COLUMN = 13;
/**
*
*/
private static final int IS_LOCKED_COLUMN = 14;
/**
* ID
@ -163,6 +168,10 @@ public class NoteItemData {
*
*/
private long mPinPriority;
/**
*
*/
private boolean mIsLocked;
/**
*
*/
@ -216,6 +225,7 @@ public class NoteItemData {
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mIsLocked = (cursor.getInt(IS_LOCKED_COLUMN) > 0);
mPhoneNumber = "";
// 如果是通话记录文件夹,获取电话号码和联系人姓名
@ -453,6 +463,14 @@ public class NoteItemData {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
/**
*
* @return
*/
public boolean isLocked() {
return mIsLocked;
}
/**
*
* @param cursor

@ -590,10 +590,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
if (data.isLocked()) {
Intent unlockIntent = new Intent(this, UnlockActivity.class);
unlockIntent.putExtra(UnlockActivity.EXTRA_NOTE_ID, data.getId());
startActivity(unlockIntent);
} else {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
}
private void openFolder(NoteItemData data) {

@ -42,6 +42,10 @@ public class NotesListItem extends LinearLayout {
*
*/
private ImageView mPinned;
/**
*
*/
private ImageView mLocked;
/**
*
*/
@ -74,6 +78,7 @@ public class NotesListItem extends LinearLayout {
// 初始化控件
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mPinned = (ImageView) findViewById(R.id.iv_pinned_icon);
mLocked = (ImageView) findViewById(R.id.iv_locked_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
@ -146,6 +151,12 @@ public class NotesListItem extends LinearLayout {
} else {
mPinned.setVisibility(View.GONE);
}
// 设置锁定图标
if (data.isLocked()) {
mLocked.setVisibility(View.VISIBLE);
} else {
mLocked.setVisibility(View.GONE);
}
}
}
// 设置修改时间

@ -0,0 +1,287 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Vibrator;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.LockPasswordUtils;
/**
*
* 6
*/
public class NumericPasswordActivity extends Activity {
private static final String TAG = "NumericPasswordActivity";
public static final String EXTRA_NOTE_ID = "note_id";
public static final String EXTRA_MODE = "mode";
public static final String MODE_SET = "set";
public static final String MODE_CHANGE = "change";
public static final String MODE_REMOVE = "remove";
public static final String MODE_VERIFY = "verify";
private TextView mPasswordDisplay;
private TextView mHintText;
private Button[] mNumberButtons = new Button[10];
private Button mDeleteButton;
private Button mCancelButton;
private long mNoteId;
private String mMode;
private WorkingNote mWorkingNote;
private String mFirstPassword;
private String mSecondPassword;
private String mOldPassword;
private StringBuilder mCurrentPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_numeric_password);
mNoteId = getIntent().getLongExtra(EXTRA_NOTE_ID, 0);
mMode = getIntent().getStringExtra(EXTRA_MODE);
if (mNoteId <= 0) {
finish();
return;
}
mWorkingNote = WorkingNote.load(this, mNoteId);
mCurrentPassword = new StringBuilder();
initViews();
setupMode();
}
private void initViews() {
mPasswordDisplay = (TextView) findViewById(R.id.tv_password_display);
mHintText = (TextView) findViewById(R.id.tv_password_hint);
mDeleteButton = (Button) findViewById(R.id.btn_delete);
mCancelButton = (Button) findViewById(R.id.btn_cancel);
mNumberButtons[0] = (Button) findViewById(R.id.btn_0);
mNumberButtons[1] = (Button) findViewById(R.id.btn_1);
mNumberButtons[2] = (Button) findViewById(R.id.btn_2);
mNumberButtons[3] = (Button) findViewById(R.id.btn_3);
mNumberButtons[4] = (Button) findViewById(R.id.btn_4);
mNumberButtons[5] = (Button) findViewById(R.id.btn_5);
mNumberButtons[6] = (Button) findViewById(R.id.btn_6);
mNumberButtons[7] = (Button) findViewById(R.id.btn_7);
mNumberButtons[8] = (Button) findViewById(R.id.btn_8);
mNumberButtons[9] = (Button) findViewById(R.id.btn_9);
for (int i = 0; i < 10; i++) {
final int number = i;
mNumberButtons[i].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onNumberPressed(number);
}
});
}
mDeleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onDeletePressed();
}
});
mCancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
private void setupMode() {
if (MODE_SET.equals(mMode)) {
mHintText.setText(R.string.numeric_password_hint_set);
} else if (MODE_CHANGE.equals(mMode)) {
mHintText.setText(R.string.numeric_password_hint_verify);
} else if (MODE_REMOVE.equals(mMode)) {
mHintText.setText(R.string.numeric_password_hint_remove);
} else if (MODE_VERIFY.equals(mMode)) {
mHintText.setText(R.string.numeric_password_hint_verify);
}
}
private void onNumberPressed(int number) {
if (mCurrentPassword.length() < 6) {
mCurrentPassword.append(number);
updatePasswordDisplay();
if (mCurrentPassword.length() == 6) {
handlePasswordComplete();
}
}
}
private void onDeletePressed() {
if (mCurrentPassword.length() > 0) {
mCurrentPassword.deleteCharAt(mCurrentPassword.length() - 1);
updatePasswordDisplay();
}
}
private void updatePasswordDisplay() {
StringBuilder display = new StringBuilder();
for (int i = 0; i < mCurrentPassword.length(); i++) {
display.append("●");
}
mPasswordDisplay.setText(display.toString());
}
private void handlePasswordComplete() {
String password = mCurrentPassword.toString();
if (MODE_SET.equals(mMode)) {
handleSetMode(password);
} else if (MODE_CHANGE.equals(mMode)) {
handleChangeMode(password);
} else if (MODE_REMOVE.equals(mMode)) {
handleRemoveMode(password);
} else if (MODE_VERIFY.equals(mMode)) {
handleVerifyMode(password);
}
}
private void handleSetMode(String password) {
if (mFirstPassword == null) {
mFirstPassword = password;
mHintText.setText(R.string.numeric_password_confirm);
mCurrentPassword.setLength(0);
updatePasswordDisplay();
} else {
mSecondPassword = password;
if (mFirstPassword.equals(mSecondPassword)) {
String encryptedPassword = LockPasswordUtils.encryptPassword(mFirstPassword);
mWorkingNote.setLocked(true);
mWorkingNote.setPasswordType(LockPasswordUtils.TYPE_NUMERIC);
mWorkingNote.setNumericPassword(encryptedPassword);
mWorkingNote.setLockPassword("");
mWorkingNote.saveNote();
Toast.makeText(this, R.string.numeric_password_success, Toast.LENGTH_SHORT).show();
finish();
} else {
mHintText.setText(R.string.numeric_password_confirm_error);
mFirstPassword = null;
mSecondPassword = null;
mCurrentPassword.setLength(0);
updatePasswordDisplay();
vibrate();
}
}
}
private void handleChangeMode(String password) {
if (mOldPassword == null) {
String encryptedInput = LockPasswordUtils.encryptPassword(password);
String storedPassword = mWorkingNote.getNumericPassword();
if (encryptedInput.equals(storedPassword)) {
mOldPassword = storedPassword;
mHintText.setText(R.string.numeric_password_hint_new);
mCurrentPassword.setLength(0);
updatePasswordDisplay();
} else {
mHintText.setText(R.string.numeric_password_error);
mCurrentPassword.setLength(0);
updatePasswordDisplay();
vibrate();
}
} else if (mFirstPassword == null) {
mFirstPassword = password;
mHintText.setText(R.string.numeric_password_confirm);
mCurrentPassword.setLength(0);
updatePasswordDisplay();
} else {
mSecondPassword = password;
if (mFirstPassword.equals(mSecondPassword)) {
String encryptedPassword = LockPasswordUtils.encryptPassword(mFirstPassword);
mWorkingNote.setNumericPassword(encryptedPassword);
mWorkingNote.saveNote();
Toast.makeText(this, R.string.numeric_password_success, Toast.LENGTH_SHORT).show();
finish();
} else {
mHintText.setText(R.string.numeric_password_confirm_error);
mFirstPassword = null;
mSecondPassword = null;
mCurrentPassword.setLength(0);
updatePasswordDisplay();
vibrate();
}
}
}
private void handleRemoveMode(String password) {
String encryptedInput = LockPasswordUtils.encryptPassword(password);
String storedPassword = mWorkingNote.getNumericPassword();
if (encryptedInput.equals(storedPassword)) {
mWorkingNote.setLocked(false);
mWorkingNote.setNumericPassword("");
mWorkingNote.setPasswordType("");
mWorkingNote.saveNote();
Toast.makeText(this, R.string.numeric_password_removed, Toast.LENGTH_SHORT).show();
finish();
} else {
mHintText.setText(R.string.numeric_password_error);
mCurrentPassword.setLength(0);
updatePasswordDisplay();
vibrate();
}
}
private void handleVerifyMode(String password) {
String encryptedInput = LockPasswordUtils.encryptPassword(password);
String storedPassword = mWorkingNote.getNumericPassword();
if (encryptedInput.equals(storedPassword)) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
finish();
} else {
mHintText.setText(R.string.numeric_password_error);
mCurrentPassword.setLength(0);
updatePasswordDisplay();
vibrate();
}
}
private void vibrate() {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (vibrator != null && vibrator.hasVibrator()) {
vibrator.vibrate(200);
}
}
}

@ -0,0 +1,273 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.LockPasswordUtils;
import java.util.List;
/**
*
* 便
*/
public class SetLockActivity extends Activity {
private static final String TAG = "SetLockActivity";
public static final String EXTRA_NOTE_ID = "note_id";
public static final String EXTRA_MODE = "mode";
public static final String MODE_SET = "set";
public static final String MODE_CHANGE = "change";
public static final String MODE_REMOVE = "remove";
private LockPatternView mLockPatternView;
private TextView mTitleText;
private TextView mHintText;
private Button mConfirmButton;
private Button mCancelButton;
private long mNoteId;
private String mMode;
private WorkingNote mWorkingNote;
private List<Integer> mFirstPattern;
private List<Integer> mSecondPattern;
private String mOldPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set_lock);
mNoteId = getIntent().getLongExtra(EXTRA_NOTE_ID, 0);
mMode = getIntent().getStringExtra(EXTRA_MODE);
if (mNoteId <= 0) {
finish();
return;
}
mWorkingNote = WorkingNote.load(this, mNoteId);
initViews();
setupMode();
}
private void initViews() {
mLockPatternView = (LockPatternView) findViewById(R.id.lock_pattern_view);
mTitleText = (TextView) findViewById(R.id.tv_lock_title);
mHintText = (TextView) findViewById(R.id.tv_lock_hint);
mConfirmButton = (Button) findViewById(R.id.btn_confirm);
mCancelButton = (Button) findViewById(R.id.btn_cancel);
mLockPatternView.setOnPatternListener(new LockPatternView.OnPatternListener() {
@Override
public void onPatternStart() {
mHintText.setText("");
}
@Override
public void onPatternCleared() {
}
@Override
public void onPatternDetected(List<Integer> pattern) {
handlePatternDetected(pattern);
}
});
mCancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
mConfirmButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleConfirm();
}
});
}
private void setupMode() {
if (MODE_SET.equals(mMode)) {
mTitleText.setText(R.string.lock_pattern_title);
mHintText.setText(R.string.lock_pattern_hint_set);
mConfirmButton.setVisibility(View.GONE);
} else if (MODE_CHANGE.equals(mMode)) {
mTitleText.setText(R.string.lock_pattern_title_change);
mHintText.setText(R.string.lock_pattern_hint_old);
mConfirmButton.setVisibility(View.GONE);
} else if (MODE_REMOVE.equals(mMode)) {
mTitleText.setText(R.string.lock_pattern_title_remove);
mHintText.setText(R.string.lock_pattern_hint_remove);
mConfirmButton.setVisibility(View.GONE);
}
}
private void handlePatternDetected(List<Integer> pattern) {
if (pattern.size() < 4) {
mHintText.setText(R.string.lock_pattern_too_short);
mLockPatternView.clearPattern();
return;
}
if (MODE_SET.equals(mMode)) {
handleSetMode(pattern);
} else if (MODE_CHANGE.equals(mMode)) {
handleChangeMode(pattern);
} else if (MODE_REMOVE.equals(mMode)) {
handleRemoveMode(pattern);
}
}
private void handleSetMode(List<Integer> pattern) {
if (mFirstPattern == null) {
mFirstPattern = pattern;
mHintText.setText(R.string.lock_pattern_confirm);
mLockPatternView.clearPattern();
} else {
mSecondPattern = pattern;
if (patternsMatch(mFirstPattern, mSecondPattern)) {
String password = LockPasswordUtils.patternToString(
LockPasswordUtils.stringToPattern(
LockPasswordUtils.patternToString(convertPatternToIntArray(mFirstPattern))
)
);
String encryptedPassword = LockPasswordUtils.encryptPassword(password);
mWorkingNote.setLocked(true);
mWorkingNote.setPasswordType(LockPasswordUtils.TYPE_GESTURE);
mWorkingNote.setLockPassword(encryptedPassword);
mWorkingNote.setNumericPassword("");
mWorkingNote.saveNote();
Toast.makeText(this, R.string.lock_pattern_success, Toast.LENGTH_SHORT).show();
finish();
} else {
mHintText.setText(R.string.lock_pattern_confirm_error);
mFirstPattern = null;
mSecondPattern = null;
mLockPatternView.clearPattern();
}
}
}
private void handleChangeMode(List<Integer> pattern) {
if (mOldPassword == null) {
String inputPassword = LockPasswordUtils.patternToString(
LockPasswordUtils.stringToPattern(
LockPasswordUtils.patternToString(convertPatternToIntArray(pattern))
)
);
String encryptedInput = LockPasswordUtils.encryptPassword(inputPassword);
String storedPassword = mWorkingNote.getLockPassword();
if (encryptedInput.equals(storedPassword)) {
mOldPassword = storedPassword;
mHintText.setText(R.string.lock_pattern_hint_new);
mLockPatternView.clearPattern();
} else {
mHintText.setText(R.string.lock_pattern_error);
mLockPatternView.clearPattern();
}
} else if (mFirstPattern == null) {
mFirstPattern = pattern;
mHintText.setText(R.string.lock_pattern_confirm);
mLockPatternView.clearPattern();
} else {
mSecondPattern = pattern;
if (patternsMatch(mFirstPattern, mSecondPattern)) {
String password = LockPasswordUtils.patternToString(
LockPasswordUtils.stringToPattern(
LockPasswordUtils.patternToString(convertPatternToIntArray(mFirstPattern))
)
);
String encryptedPassword = LockPasswordUtils.encryptPassword(password);
mWorkingNote.setLockPassword(encryptedPassword);
mWorkingNote.saveNote();
Toast.makeText(this, R.string.lock_pattern_success, Toast.LENGTH_SHORT).show();
finish();
} else {
mHintText.setText(R.string.lock_pattern_confirm_error);
mFirstPattern = null;
mSecondPattern = null;
mLockPatternView.clearPattern();
}
}
}
private void handleRemoveMode(List<Integer> pattern) {
String inputPassword = LockPasswordUtils.patternToString(
LockPasswordUtils.stringToPattern(
LockPasswordUtils.patternToString(convertPatternToIntArray(pattern))
)
);
String encryptedInput = LockPasswordUtils.encryptPassword(inputPassword);
String storedPassword = mWorkingNote.getLockPassword();
if (encryptedInput.equals(storedPassword)) {
mWorkingNote.setLocked(false);
mWorkingNote.setLockPassword("");
mWorkingNote.setPasswordType("");
mWorkingNote.saveNote();
Toast.makeText(this, R.string.lock_pattern_removed, Toast.LENGTH_SHORT).show();
finish();
} else {
mHintText.setText(R.string.lock_pattern_error);
mLockPatternView.clearPattern();
}
}
private void handleConfirm() {
finish();
}
private boolean patternsMatch(List<Integer> pattern1, List<Integer> pattern2) {
if (pattern1 == null || pattern2 == null) {
return false;
}
if (pattern1.size() != pattern2.size()) {
return false;
}
for (int i = 0; i < pattern1.size(); i++) {
if (!pattern1.get(i).equals(pattern2.get(i))) {
return false;
}
}
return true;
}
private int[] convertPatternToIntArray(List<Integer> pattern) {
int[] array = new int[pattern.size()];
for (int i = 0; i < pattern.size(); i++) {
array[i] = pattern.get(i);
}
return array;
}
}

@ -0,0 +1,213 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Vibrator;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.LockPasswordUtils;
import java.util.List;
/**
*
* 便
*/
public class UnlockActivity extends Activity {
private static final String TAG = "UnlockActivity";
public static final String EXTRA_NOTE_ID = "note_id";
private LockPatternView mLockPatternView;
private TextView mTitleText;
private TextView mHintText;
private Button mCancelButton;
private long mNoteId;
private WorkingNote mWorkingNote;
private String mPasswordType;
private String mEncryptedPassword;
private int mFailedAttempts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_unlock);
mNoteId = getIntent().getLongExtra(EXTRA_NOTE_ID, 0);
if (mNoteId <= 0) {
finish();
return;
}
mWorkingNote = WorkingNote.load(this, mNoteId);
mPasswordType = mWorkingNote.getPasswordType();
if (!mWorkingNote.isLocked()) {
unlockNote();
return;
}
if (TextUtils.isEmpty(mPasswordType)) {
mPasswordType = LockPasswordUtils.TYPE_GESTURE;
}
if (LockPasswordUtils.TYPE_GESTURE.equals(mPasswordType)) {
mEncryptedPassword = mWorkingNote.getLockPassword();
} else if (LockPasswordUtils.TYPE_NUMERIC.equals(mPasswordType)) {
mEncryptedPassword = mWorkingNote.getNumericPassword();
}
if (TextUtils.isEmpty(mEncryptedPassword)) {
unlockNote();
return;
}
initViews();
}
private void initViews() {
mLockPatternView = (LockPatternView) findViewById(R.id.lock_pattern_view);
mTitleText = (TextView) findViewById(R.id.tv_unlock_title);
mHintText = (TextView) findViewById(R.id.tv_unlock_hint);
mCancelButton = (Button) findViewById(R.id.btn_cancel);
mTitleText.setText(getString(R.string.lock_note_title, mWorkingNote.getContent().substring(0,
Math.min(20, mWorkingNote.getContent().length()))));
if (LockPasswordUtils.TYPE_GESTURE.equals(mPasswordType)) {
setupGestureUnlock();
} else if (LockPasswordUtils.TYPE_NUMERIC.equals(mPasswordType)) {
setupNumericUnlock();
}
mCancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
private void setupGestureUnlock() {
mHintText.setText(R.string.lock_note_hint);
mLockPatternView.setVisibility(View.VISIBLE);
mLockPatternView.setOnPatternListener(new LockPatternView.OnPatternListener() {
@Override
public void onPatternStart() {
mHintText.setText("");
}
@Override
public void onPatternCleared() {
}
@Override
public void onPatternDetected(List<Integer> pattern) {
handleGesturePatternDetected(pattern);
}
});
}
private void setupNumericUnlock() {
mHintText.setText(R.string.numeric_password_hint_verify);
mLockPatternView.setVisibility(View.GONE);
showNumericPasswordActivity();
}
private void showNumericPasswordActivity() {
Intent intent = new Intent(this, NumericPasswordActivity.class);
intent.putExtra(NumericPasswordActivity.EXTRA_NOTE_ID, mNoteId);
intent.putExtra(NumericPasswordActivity.EXTRA_MODE, NumericPasswordActivity.MODE_VERIFY);
startActivity(intent);
finish();
}
private void handleGesturePatternDetected(List<Integer> pattern) {
if (pattern.size() < 4) {
mHintText.setText(R.string.lock_pattern_too_short);
mLockPatternView.clearPattern();
return;
}
String inputPassword = LockPasswordUtils.patternToString(
LockPasswordUtils.stringToPattern(
LockPasswordUtils.patternToString(convertPatternToIntArray(pattern))
)
);
String encryptedInput = LockPasswordUtils.encryptPassword(inputPassword);
if (encryptedInput.equals(mEncryptedPassword)) {
unlockNote();
} else {
mFailedAttempts++;
mHintText.setText(R.string.lock_pattern_error);
mLockPatternView.clearPattern();
vibrate();
if (mFailedAttempts >= 5) {
mHintText.setText(getString(R.string.lock_pattern_too_many_attempts));
mCancelButton.setEnabled(false);
mLockPatternView.setEnabled(false);
new android.os.Handler().postDelayed(new Runnable() {
@Override
public void run() {
mFailedAttempts = 0;
mHintText.setText(R.string.lock_note_hint);
mCancelButton.setEnabled(true);
mLockPatternView.setEnabled(true);
}
}, 30000);
}
}
}
private void unlockNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
finish();
}
private void vibrate() {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (vibrator != null && vibrator.hasVibrator()) {
vibrator.vibrate(200);
}
}
private int[] convertPatternToIntArray(List<Integer> pattern) {
int[] array = new int[pattern.size()];
for (int i = 0; i < pattern.size(); i++) {
array[i] = pattern.get(i);
}
return array;
}
}

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="@android:color/white">
<TextView
android:id="@+id/tv_password_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/numeric_password_title"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginTop="32dp"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/tv_password_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:gravity="center"
android:layout_marginBottom="32dp"
android:textColor="@color/secondary_text_dark" />
<TextView
android:id="@+id/tv_password_display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="32sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="48dp"
android:textColor="@color/primary_text_dark" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/btn_1"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="1"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_2"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="2"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_3"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="3"
android:textSize="24sp"
android:layout_margin="4dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/btn_4"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="4"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_5"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="5"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_6"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="6"
android:textSize="24sp"
android:layout_margin="4dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/btn_7"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="7"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_8"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="8"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_9"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="9"
android:textSize="24sp"
android:layout_margin="4dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/btn_delete"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="←"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_0"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="0"
android:textSize="24sp"
android:layout_margin="4dp" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="@android:string/cancel"
android:textSize="16sp"
android:layout_margin="4dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="@android:color/white">
<TextView
android:id="@+id/tv_lock_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginTop="32dp"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/tv_lock_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:gravity="center"
android:layout_marginBottom="16dp"
android:textColor="@color/secondary_text_dark" />
<RadioGroup
android:id="@+id/rg_password_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="16dp"
android:visibility="gone">
<RadioButton
android:id="@+id/rb_gesture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lock_type_gesture"
android:checked="true" />
<RadioButton
android:id="@+id/rb_numeric"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lock_type_numeric" />
</RadioGroup>
<net.micode.notes.ui.LockPatternView
android:id="@+id/lock_pattern_view"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="32dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/cancel"
android:minWidth="120dp"
android:layout_marginEnd="16dp" />
<Button
android:id="@+id/btn_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/ok"
android:minWidth="120dp"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="@android:color/white">
<TextView
android:id="@+id/tv_unlock_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lock_note_title_lock"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginTop="32dp"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/tv_unlock_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:gravity="center"
android:layout_marginBottom="32dp"
android:textColor="@color/secondary_text_dark" />
<net.micode.notes.ui.LockPatternView
android:id="@+id/lock_pattern_view"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="32dp" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/cancel"
android:minWidth="120dp"
android:layout_gravity="center_horizontal" />
</LinearLayout>

@ -83,4 +83,12 @@
android:layout_gravity="top|left"
android:src="@drawable/ic_pinned"
android:visibility="gone"/>
<ImageView
android:id="@+id/iv_locked_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:src="@android:drawable/ic_lock_lock"
android:visibility="gone"/>
</FrameLayout>

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
@ -17,36 +16,34 @@
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_new_note"
android:title="@string/notelist_menu_new"/>
<item
android:id="@+id/menu_delete"
android:title="@string/menu_delete"/>
<item
android:id="@+id/menu_font_size"
android:title="@string/menu_font_size"/>
<item
android:id="@+id/menu_list_mode"
android:title="@string/menu_list_mode" />
<item
android:id="@+id/menu_share"
android:title="@string/menu_share"/>
<item
android:id="@+id/menu_send_to_desktop"
android:title="@string/menu_send_to_desktop"/>
<item
android:id="@+id/menu_alert"
android:title="@string/menu_alert" />
<item
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind" />
</menu>
<item
android:id="@+id/menu_set_password"
android:title="@string/menu_set_password" />
<item
android:id="@+id/menu_remove_lock"
android:title="@string/menu_remove_lock" />
</menu>

@ -118,6 +118,50 @@
<string name="preferences_toast_success_set_accout">%1$s has been set as the sync account</string>
<string name="preferences_bg_random_appear_title">New note background color random</string>
<string name="menu_set_lock">Set password</string>
<string name="menu_remove_lock">Remove password</string>
<string name="lock_pattern_title">Set gesture password</string>
<string name="lock_pattern_title_change">Change gesture password</string>
<string name="lock_pattern_title_remove">Remove gesture password</string>
<string name="lock_pattern_hint_set">Draw a pattern to set your password</string>
<string name="lock_pattern_hint_old">Draw your current password</string>
<string name="lock_pattern_hint_new">Draw a new pattern</string>
<string name="lock_pattern_hint_remove">Draw your current password to remove it</string>
<string name="lock_pattern_confirm">Confirm your pattern</string>
<string name="lock_pattern_error">Wrong password, please try again</string>
<string name="lock_pattern_success">Password set successfully</string>
<string name="lock_pattern_removed">Password removed</string>
<string name="lock_pattern_too_short">Pattern too short, connect at least 4 dots</string>
<string name="lock_pattern_confirm_error">Patterns do not match</string>
<string name="lock_pattern_too_many_attempts">Too many failed attempts, please wait 30 seconds</string>
<string name="lock_note_title">Unlock note</string>
<string name="lock_note_title_lock">Lock note</string>
<string name="lock_note_hint">Draw gesture password to unlock</string>
<string name="menu_set_gesture_lock">Set gesture password</string>
<string name="menu_set_numeric_lock">Set numeric password</string>
<string name="menu_change_gesture_lock">Change gesture password</string>
<string name="menu_change_numeric_lock">Change numeric password</string>
<string name="numeric_password_title">Set numeric password</string>
<string name="numeric_password_hint_set">Enter 6-digit password</string>
<string name="numeric_password_hint_verify">Enter current numeric password</string>
<string name="numeric_password_hint_new">Enter new numeric password</string>
<string name="numeric_password_hint_remove">Enter numeric password to remove it</string>
<string name="numeric_password_confirm">Confirm your password</string>
<string name="numeric_password_error">Wrong password, please try again</string>
<string name="numeric_password_success">Password set successfully</string>
<string name="numeric_password_removed">Password removed</string>
<string name="numeric_password_too_short">Password must be 6 digits</string>
<string name="numeric_password_confirm_error">Passwords do not match</string>
<string name="lock_type_gesture">Gesture password</string>
<string name="lock_type_numeric">Numeric password</string>
<string name="select_lock_type">Select password type</string>
<string name="cannot_use_both_passwords">Cannot use both password types</string>
<string name="menu_set_password">Set password</string>
<string name="dialog_set_password_title">Set password</string>
<string name="dialog_set_gesture_password">Gesture password</string>
<string name="dialog_set_numeric_password">Numeric password</string>
<string name="button_delete">Delete</string>
<string name="call_record_folder_name">Call notes</string>
<string name="hint_foler_name">Input name</string>

Loading…
Cancel
Save