添加便签内插入图片的功能,修复Settings不能正常启动的bug #8

Merged
plyf8vez7 merged 2 commits from fancanyu_branch into master 4 months ago

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.INTERNET" />
@ -10,6 +11,7 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@ -20,6 +22,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.Notesmaster"
tools:targetApi="31">
<activity
android:name=".ui.NotesListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -29,6 +32,7 @@
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@ -81,7 +85,6 @@
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
@ -123,11 +126,17 @@
android:launchMode="singleInstance"
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
</activity>
<activity
android:name=".ui.PasswordVerifyActivity"
android:label="@string/app_name"
android:theme="@style/NoteTheme"
android:exported="true" >
</activity>
<activity
android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.Light" >
@ -142,15 +151,15 @@
android:name="android.app.default_searchable"
android:value=".ui.NoteEditActivity" />
<!-- <activity-->
<!-- android:name=".MainActivity"-->
<!-- android:exported="true">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <activity-->
<!-- android:name=".MainActivity"-->
<!-- android:exported="true">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
</application>

@ -1,26 +1,6 @@
<?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");
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.micode.notes"
android:versionCode="1"
android:versionName="0.1" >
<uses-sdk android:minSdkVersion="14" />
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
@ -33,8 +13,16 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:icon="@drawable/icon_app"
android:label="@string/app_name" >
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Notesmaster"
tools:targetApi="31">
<activity
android:name=".ui.NotesListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -42,7 +30,8 @@
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan" >
android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -50,20 +39,22 @@
</intent-filter>
</activity>
<activity
android:name=".ui.NoteEditActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/NoteTheme" >
android:theme="@style/NoteTheme"
android:exported="true">
<intent-filter>
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter>
</intent-filter >
<intent-filter>
<intent-filter >
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" />
@ -80,6 +71,7 @@
android:resource="@xml/searchable" />
</activity>
<provider
android:name="net.micode.notes.data.NotesProvider"
android:authorities="micode_notes"
@ -87,10 +79,12 @@
<receiver
android:name=".widget.NoteWidgetProvider_2x"
android:label="@string/app_widget2x2" >
android:label="@string/app_widget2x2"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
@ -100,7 +94,8 @@
</receiver>
<receiver
android:name=".widget.NoteWidgetProvider_4x"
android:label="@string/app_widget4x4" >
android:label="@string/app_widget4x4"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@ -113,7 +108,8 @@
android:resource="@xml/widget_4x_info" />
</receiver>
<receiver android:name=".ui.AlarmInitReceiver" >
<receiver android:name=".ui.AlarmInitReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
@ -130,9 +126,17 @@
android:launchMode="singleInstance"
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
</activity>
<activity
android:name=".ui.PasswordVerifyActivity"
android:label="@string/app_name"
android:theme="@style/NoteTheme"
android:exported="true" >
</activity>
<activity
android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.Light" >
@ -146,5 +150,17 @@
<meta-data
android:name="android.app.default_searchable"
android:value=".ui.NoteEditActivity" />
<!-- <activity-->
<!-- android:name=".MainActivity"-->
<!-- android:exported="true">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
</application>
</manifest>
</manifest>

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Notesmaster"
tools:targetApi="31">
<activity
android:name=".ui.NotesListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.NoteEditActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter >
<intent-filter >
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
<provider
android:name="net.micode.notes.data.NotesProvider"
android:authorities="micode_notes"
android:multiprocess="true" />
<receiver
android:name=".widget.NoteWidgetProvider_2x"
android:label="@string/app_widget2x2"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_2x_info" />
</receiver>
<receiver
android:name=".widget.NoteWidgetProvider_4x"
android:label="@string/app_widget4x4"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_4x_info" />
</receiver>
<receiver android:name=".ui.AlarmInitReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name="net.micode.notes.ui.AlarmReceiver"
android:process=":remote" >
</receiver>
<activity
android:name=".ui.AlarmAlertActivity"
android:label="@string/app_name"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
</activity>
<activity
android:name=".ui.PasswordVerifyActivity"
android:label="@string/app_name"
android:theme="@style/NoteTheme"
android:exported="true" >
</activity>
<activity
android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.Light" >
</activity>
<service
android:name="net.micode.notes.gtask.remote.GTaskSyncService"
android:exported="false" >
</service>
<meta-data
android:name="android.app.default_searchable"
android:value=".ui.NoteEditActivity" />
<!-- <activity-->
<!-- android:name=".MainActivity"-->
<!-- android:exported="true">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
</application>
</manifest>

@ -1,19 +1,3 @@
/*
* 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;
@ -78,77 +62,84 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
/**
*
* ////
*/
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
// 异步查询标记:笔记列表查询
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
// 异步查询标记:文件夹列表查询
private static final int FOLDER_LIST_QUERY_TOKEN = 1;
// 文件夹上下文菜单:删除
private static final int MENU_FOLDER_DELETE = 0;
// 文件夹上下文菜单:查看
private static final int MENU_FOLDER_VIEW = 1;
// 文件夹上下文菜单:重命名
private static final int MENU_FOLDER_CHANGE_NAME = 2;
// 偏好设置:首次使用引导标记
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
private enum ListEditState {
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
private ListEditState mState;
private BackgroundQueryHandler mBackgroundQueryHandler;
private NotesListAdapter mNotesListAdapter;
private ListView mNotesListView;
private Button mAddNewNote;
private boolean mDispatch;
private int mOriginY;
private int mDispatchY;
private TextView mTitleBar;
private long mCurrentFolderId;
private ContentResolver mContentResolver;
private ModeCallback mModeCallBack;
// 请求码:打开已有笔记
private final static int REQUEST_CODE_OPEN_NODE = 102;
// 请求码:新建笔记
private final static int REQUEST_CODE_NEW_NODE = 103;
// 日志标记
private static final String TAG = "NotesListActivity";
// 列表滚动速率
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;
private NoteItemData mFocusNoteDataItem;
// 普通文件夹查询条件
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";
// 根文件夹查询条件
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "
+ NoteColumns.NOTES_COUNT + ">0)";
private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_NEW_NODE = 103;
/**
*
* NOTE_LIST/
* SUB_FOLDER
* CALL_RECORD_FOLDER
*/
private enum ListEditState {
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
private ListEditState mState; // 当前列表编辑状态
private BackgroundQueryHandler mBackgroundQueryHandler; // 异步查询处理器
private NotesListAdapter mNotesListAdapter; // 笔记列表适配器
private ListView mNotesListView; // 笔记列表视图
private Button mAddNewNote; // 新建笔记按钮
private boolean mDispatch; // 触摸事件透传标记
private int mOriginY; // 触摸事件原始Y坐标
private int mDispatchY; // 触摸事件透传Y坐标
private TextView mTitleBar; // 标题栏(子文件夹/通话记录文件夹显示)
private long mCurrentFolderId; // 当前所在文件夹ID
private ContentResolver mContentResolver; // 内容解析器
private ModeCallback mModeCallBack; // 多选模式回调
private NoteItemData mFocusNoteDataItem; // 长按选中的笔记/文件夹数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 检查密码锁是否启用
if (PasswordVerifyActivity.isPasswordEnabled(this)) {
Intent intent = new Intent(this, PasswordVerifyActivity.class);
startActivity(intent);
finish();
return;
}
setContentView(R.layout.note_list);
initResources();
/**
* Insert an introduction when user firstly use this application
*/
// 首次使用时添加引导笔记
setAppInfoFromRawRes();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 笔记编辑完成后刷新列表
if (resultCode == RESULT_OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
mNotesListAdapter.changeCursor(null);
@ -157,13 +148,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
* 使
* raw
*/
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
StringBuilder sb = new StringBuilder();
InputStream in = null;
try {
in = getResources().openRawResource(R.raw.introduction);
in = getResources().openRawResource(R.raw.introduction);
if (in != null) {
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
@ -184,7 +179,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@ -206,14 +200,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
protected void onStart() {
super.onStart();
// 启动异步查询加载笔记列表
startAsyncNotesListQuery();
}
/**
*
*/
private void initResources() {
mContentResolver = this.getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mNotesListView = (ListView) findViewById(R.id.notes_list);
// 添加列表底部视图
mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null),
null, false);
mNotesListView.setOnItemClickListener(new OnListItemClickListener());
@ -231,15 +230,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mModeCallBack = new ModeCallback();
}
/**
*
*
*/
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu;
private ActionMode mActionMode;
private MenuItem mMoveMenu;
private DropdownMenu mDropDownMenu; // 下拉菜单(全选/取消全选)
private ActionMode mActionMode; // 动作模式
private MenuItem mMoveMenu; // 移动菜单选项
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
getMenuInflater().inflate(R.menu.note_list_options, menu);
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
mMoveMenu = menu.findItem(R.id.move);
// 通话记录文件夹或无用户文件夹时,隐藏移动选项
if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER
|| DataUtils.getUserFolderCount(mContentResolver) == 0) {
mMoveMenu.setVisible(false);
@ -252,6 +256,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mNotesListView.setLongClickable(false);
mAddNewNote.setVisibility(View.GONE);
// 设置自定义动作模式视图(下拉菜单)
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
R.layout.note_list_dropdown_menu, null);
mode.setCustomView(customView);
@ -264,14 +269,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
updateMenu();
return true;
}
});
return true;
}
/**
*
*/
private void updateMenu() {
int selectedCount = mNotesListAdapter.getSelectedCount();
// Update dropdown menu
String format = getResources().getString(R.string.menu_select_title, selectedCount);
mDropDownMenu.setTitle(format);
MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);
@ -287,16 +293,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
return false;
}
public void onDestroyActionMode(ActionMode mode) {
// 退出多选模式,恢复页面状态
mNotesListAdapter.setChoiceMode(false);
mNotesListView.setLongClickable(true);
mAddNewNote.setVisibility(View.VISIBLE);
@ -307,7 +312,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked) {
boolean checked) {
mNotesListAdapter.setCheckedItem(position, checked);
updateMenu();
}
@ -321,22 +326,24 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
switch (item.getItemId()) {
case R.id.delete:
// 弹出批量删除确认对话框
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_notes,
mNotesListAdapter.getSelectedCount()));
mNotesListAdapter.getSelectedCount()));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();
}
});
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case R.id.move:
// 查询目标文件夹列表
startQueryDestinationFolders();
break;
default:
@ -346,6 +353,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
*
*
*/
private class NewNoteOnTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
@ -356,22 +367,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
int newNoteViewHeight = mAddNewNote.getHeight();
int start = screenHeight - newNoteViewHeight;
int eventY = start + (int) event.getY();
/**
* Minus TitleBar's height
*/
// 子文件夹状态下,扣除标题栏高度
if (mState == ListEditState.SUB_FOLDER) {
eventY -= mTitleBar.getHeight();
start -= mTitleBar.getHeight();
}
/**
* HACKME:When click the transparent part of "New Note" button, dispatch
* the event to the list view behind this button. The transparent part of
* "New Note" button could be expressed by formula y=-0.12x+94Unit:pixel
* and the line top of the button. The coordinate based on left of the "New
* Note" button. The 94 represents maximum height of the transparent part.
* Notice that, if the background of the button changes, the formula should
* also change. This is very bad, just for the UI designer's strong requirement.
*/
// 透明区域触摸事件透传到列表
if (event.getY() < (event.getX() * (-0.12) + 94)) {
View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1
- mNotesListView.getFooterViewsCount());
@ -408,15 +409,22 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
};
/**
* /
*/
private void startAsyncNotesListQuery() {
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION;
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {
String.valueOf(mCurrentFolderId)
String.valueOf(mCurrentFolderId)
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
/**
*
* /
*/
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
@ -426,10 +434,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case FOLDER_NOTE_LIST_QUERY_TOKEN:
// 更新笔记列表数据
mNotesListAdapter.changeCursor(cursor);
break;
case FOLDER_LIST_QUERY_TOKEN:
if (cursor != null && cursor.getCount() > 0) {
// 显示文件夹选择菜单
showFolderListMenu(cursor);
} else {
Log.e(TAG, "Query folder failed");
@ -441,6 +451,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
*
* @param cursor
*/
private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(R.string.menu_title_select_folder);
@ -462,6 +476,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
builder.show();
}
/**
*
*
*/
private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
@ -469,20 +487,22 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
}
/**
*
*
*/
private void batchDelete() {
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
HashSet<AppWidgetAttribute> widgets = mNotesListAdapter.getSelectedWidget();
if (!isSyncMode()) {
// if not synced, delete notes directly
if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter
// 非同步模式:直接删除
if (!DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter
.getSelectedItemIds())) {
} else {
Log.e(TAG, "Delete notes error, should not happens");
}
} else {
// in sync mode, we'll move the deleted note into the trash
// folder
// 同步模式:移至回收站
if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter
.getSelectedItemIds(), Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens");
@ -493,6 +513,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> widgets) {
// 更新关联的桌面小组件
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
@ -506,6 +527,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}.execute();
}
/**
*
* @param folderId ID
*/
private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
@ -517,12 +542,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
HashSet<AppWidgetAttribute> widgets = DataUtils.getFolderNoteWidget(mContentResolver,
folderId);
if (!isSyncMode()) {
// if not synced, delete folder directly
// 非同步模式:直接删除
DataUtils.batchDeleteNotes(mContentResolver, ids);
} else {
// in sync mode, we'll move the deleted folder into the trash folder
// 同步模式:移至回收站
DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER);
}
// 更新关联的桌面小组件
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
@ -533,6 +559,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
*
* @param data
*/
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
@ -540,15 +570,21 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
/**
*
* @param data
*/
private void openFolder(NoteItemData data) {
mCurrentFolderId = data.getId();
startAsyncNotesListQuery();
// 更新页面状态
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mState = ListEditState.CALL_RECORD_FOLDER;
mAddNewNote.setVisibility(View.GONE);
} else {
mState = ListEditState.SUB_FOLDER;
}
// 设置标题栏
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mTitleBar.setText(R.string.call_record_folder_name);
} else {
@ -557,6 +593,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mTitleBar.setVisibility(View.VISIBLE);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_new_note:
@ -567,6 +604,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
*
*/
private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
@ -574,16 +614,25 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
*
* @param view
*/
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
/**
* /
* @param create truefalse
*/
private void showCreateOrModifyFolderDialog(final boolean create) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
final EditText etName = (EditText) view.findViewById(R.id.et_foler_name);
showSoftInput();
// 修改文件夹:填充原有名称
if (!create) {
if (mFocusNoteDataItem != null) {
etName.setText(mFocusNoteDataItem.getSnippet());
@ -610,12 +659,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
public void onClick(View v) {
hideSoftInput(etName);
String name = etName.getText().toString();
// 检查文件夹名称是否已存在
if (DataUtils.checkVisibleFolderName(mContentResolver, name)) {
Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name),
Toast.LENGTH_LONG).show();
etName.setSelection(0, etName.length());
return;
}
// 修改文件夹名称
if (!create) {
if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
@ -624,10 +675,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
values.put(NoteColumns.LOCAL_MODIFIED, 1);
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID
+ "=?", new String[] {
String.valueOf(mFocusNoteDataItem.getId())
String.valueOf(mFocusNoteDataItem.getId())
});
}
} else if (!TextUtils.isEmpty(name)) {
}
// 新建文件夹
else if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
@ -637,16 +690,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
});
// 文本为空时禁用确认按钮
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
}
/**
* When the name edit text is null, disable the positive button
*/
etName.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
@ -658,14 +707,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}
@Override
public void onBackPressed() {
// 返回上级目录逻辑
switch (mState) {
case SUB_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
@ -688,6 +736,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
*
* @param appWidgetId ID
* @param appWidgetType
*/
private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
@ -700,13 +753,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
appWidgetId
appWidgetId
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
/**
*
*/
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) {
@ -737,6 +793,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
openFolder(mFocusNoteDataItem);
break;
case MENU_FOLDER_DELETE:
// 弹出文件夹删除确认对话框
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
@ -763,9 +820,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
// 根据当前状态加载对应菜单
if (mState == ListEditState.NOTE_LIST) {
getMenuInflater().inflate(R.menu.note_list, menu);
// set sync or sync_cancel
menu.findItem(R.id.menu_sync).setTitle(
GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync);
} else if (mState == ListEditState.SUB_FOLDER) {
@ -781,15 +838,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_new_folder: {
case R.id.menu_new_folder:
// 显示新建文件夹对话框
showCreateOrModifyFolderDialog(true);
break;
}
case R.id.menu_export_text: {
case R.id.menu_export_text:
// 导出笔记为文本
exportNoteToText();
break;
}
case R.id.menu_sync: {
case R.id.menu_sync:
// 同步/取消同步
if (isSyncMode()) {
if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {
GTaskSyncService.startSync(this);
@ -800,16 +858,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
startPreferenceActivity();
}
break;
}
case R.id.menu_setting: {
case R.id.menu_setting:
// 打开设置页面
startPreferenceActivity();
break;
}
case R.id.menu_new_note: {
case R.id.menu_new_note:
// 新建笔记
createNewNote();
break;
}
case R.id.menu_search:
// 打开搜索
onSearchRequested();
break;
default:
@ -824,6 +882,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true;
}
/**
*
*/
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() {
@ -835,6 +896,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
protected void onPostExecute(Integer result) {
// 根据导出结果显示对应提示
if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
@ -866,21 +928,33 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}.execute();
}
/**
*
* @return
*/
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
/**
*
*/
private void startPreferenceActivity() {
Activity from = getParent() != null ? getParent() : this;
Intent intent = new Intent(from, NotesPreferenceActivity.class);
from.startActivityIfNeeded(intent, -1);
}
/**
*
* /
*/
private class OnListItemClickListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
NoteItemData item = ((NotesListItem) view).getItemData();
// 多选模式下,切换选中状态
if (mNotesListAdapter.isInChoiceMode()) {
if (item.getType() == Notes.TYPE_NOTE) {
position = position - mNotesListView.getHeaderViewsCount();
@ -890,6 +964,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return;
}
// 根据当前状态处理点击事件
switch (mState) {
case NOTE_LIST:
if (item.getType() == Notes.TYPE_FOLDER
@ -917,10 +992,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
/**
*
*/
private void startQueryDestinationFolders() {
String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?";
selection = (mState == ListEditState.NOTE_LIST) ? selection:
"(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")";
"(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")";
mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN,
null,
@ -935,9 +1013,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
NoteColumns.MODIFIED_DATE + " DESC");
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
mFocusNoteDataItem = ((NotesListItem) view).getItemData();
// 笔记长按:进入多选模式
if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) {
if (mNotesListView.startActionMode(mModeCallBack) != null) {
mModeCallBack.onItemCheckedStateChanged(null, position, id, true);
@ -945,10 +1025,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} else {
Log.e(TAG, "startActionMode fails");
}
} else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
}
// 文件夹长按:创建上下文菜单
else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener);
}
}
return false;
}
}
}

@ -1,19 +1,3 @@
/*
* 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.accounts.Account;
@ -47,53 +31,62 @@ import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
*
*
*/
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences";
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账号名称偏好键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 最后同步时间偏好键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色设置偏好键
private static final String AUTHORITIES_FILTER_KEY = "authorities";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账号偏好分类键
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账号权限过滤键
private PreferenceCategory mAccountCategory;
private GTaskReceiver mReceiver;
private Account[] mOriAccounts;
private boolean mHasAddedAccount;
private PreferenceCategory mAccountCategory; // 账号设置偏好分类
private GTaskReceiver mReceiver; // 同步状态广播接收器
private Account[] mOriAccounts; // 原始谷歌账号数组
private boolean mHasAddedAccount; // 是否新增账号标记
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
/* using the app icon for navigation */
// 设置ActionBar返回按钮
getActionBar().setDisplayHomeAsUpEnabled(true);
// 加载偏好设置资源
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
// 注册同步服务广播过滤器
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
// 添加设置页面头部视图
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
// 为密码锁设置选项添加点击事件
Preference passwordSettingPref = findPreference("pref_key_password_setting");
if (passwordSettingPref != null) {
passwordSettingPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
showPasswordSettingDialog();
return true;
}
});
}
}
@Override
protected void onResume() {
super.onResume();
// need to set sync account automatically if user has added a new
// account
// 检测是否新增账号,自动配置同步账号
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
@ -113,17 +106,22 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
// 刷新页面UI
refreshUI();
}
@Override
protected void onDestroy() {
// 注销广播接收器,释放资源
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
/**
*
*/
private void loadAccountPreference() {
mAccountCategory.removeAll();
@ -135,16 +133,15 @@ public class NotesPreferenceActivity extends PreferenceActivity {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// the first time to set account
// 首次设置账号,显示账号选择对话框
showSelectAccountAlertDialog();
} else {
// if the account has already been set, we need to promp
// user about the risk
// 已设置账号,显示更换账号确认对话框
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
@ -154,11 +151,14 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mAccountCategory.addPreference(accountPref);
}
/**
*
*/
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// set button state
// 根据同步状态设置按钮样式和点击事件
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
@ -174,9 +174,10 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
});
}
// 未设置账号时禁用同步按钮
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// set last sync time
// 设置最后同步时间展示
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
@ -193,14 +194,21 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
/**
* UI+
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/**
* /
*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
@ -216,6 +224,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mOriAccounts = accounts;
mHasAddedAccount = false;
// 展示已有谷歌账号列表
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
@ -237,6 +246,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
});
}
// 添加新增账号视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
@ -246,7 +256,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
@ -254,9 +264,13 @@ public class NotesPreferenceActivity extends PreferenceActivity {
});
}
/**
* //
*/
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
@ -265,6 +279,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
// 对话框选项
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
@ -283,11 +298,19 @@ public class NotesPreferenceActivity extends PreferenceActivity {
dialogBuilder.show();
}
/**
*
* @return
*/
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
/**
*
* @param account
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
@ -299,10 +322,10 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
editor.commit();
// clean up last sync time
// 清空最后同步时间
setLastSyncTime(this, 0);
// clean up local gtask related info
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
@ -318,6 +341,9 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
/**
*
*/
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
@ -329,7 +355,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
editor.commit();
// clean up local gtask related info
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
@ -340,12 +366,22 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}).start();
}
/**
*
* @param context
* @return
*/
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
/**
*
* @param context
* @param time
*/
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
@ -354,12 +390,21 @@ public class NotesPreferenceActivity extends PreferenceActivity {
editor.commit();
}
/**
*
* @param context
* @return
*/
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
/**
* 广
* 广UI
*/
private class GTaskReceiver extends BroadcastReceiver {
@Override
@ -370,13 +415,16 @@ public class NotesPreferenceActivity extends PreferenceActivity {
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
/**
* ActionBar
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// 返回笔记列表首页
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
@ -385,4 +433,4 @@ public class NotesPreferenceActivity extends PreferenceActivity {
return false;
}
}
}
}

@ -1,19 +1,3 @@
/*
* 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.accounts.Account;
@ -48,138 +32,61 @@ import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
* 便Google
*
*
* - Google
* -
* -
* -
*
*
* PreferenceActivity
* AccountManagerGoogle
* 广
* 使SharedPreferences
*
*
* - Google
* -
* -
* -
*
*
* 1. 使
* 2.
* 3.
* 4.
*
*
* - AccountManager
* - GTaskSyncServiceGoogle
* - SharedPreferences
* - BroadcastReceiver
*
*
* 1. GET_ACCOUNTS访
* 2.
* 3.
* 4.
*
* @see PreferenceActivity Android
* @see AccountManager
* @see GTaskSyncService Google
*
*
*/
public class NotesPreferenceActivity extends PreferenceActivity {
/**
*
* SharedPreferences
*/
public static final String PREFERENCE_NAME = "notes_preferences";
/**
*
* Google
*/
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
/**
*
*
*/
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
/**
*
* 便
*/
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
/**
*
* XML
*/
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
/**
*
* Gmail
*/
private static final String AUTHORITIES_FILTER_KEY = "authorities";
private PreferenceCategory mAccountCategory;
public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账号名称偏好键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 最后同步时间偏好键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色设置偏好键
private GTaskReceiver mReceiver;
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账号偏好分类键
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账号权限过滤键
private Account[] mOriAccounts;
private PreferenceCategory mAccountCategory; // 账号设置偏好分类
private GTaskReceiver mReceiver; // 同步状态广播接收器
private Account[] mOriAccounts; // 原始谷歌账号数组
private boolean mHasAddedAccount; // 是否新增账号标记
private boolean mHasAddedAccount;
/**
*
* 广
*/
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
/**
* ActionBar
* 便
*/
/* using the app icon for navigation */
// 设置ActionBar返回按钮
getActionBar().setDisplayHomeAsUpEnabled(true);
/**
* XML
* R.xml.preferences
*
*/
// 加载偏好设置资源
addPreferencesFromResource(R.xml.preferences);
/**
*
*
*/
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
/**
* 广
*
*/
mReceiver = new GTaskReceiver();
// 注册同步服务广播过滤器
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
/**
*
*
*/
// 添加设置页面头部视图
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
// 为密码锁设置选项添加点击事件
Preference passwordSettingPref = findPreference("pref_key_password_setting");
if (passwordSettingPref != null) {
passwordSettingPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
showPasswordSettingDialog();
return true;
}
});
}
}
/**
*
*
*/
@Override
protected void onResume() {
super.onResume();
// need to set sync account automatically if user has added a new
// account
// 检测是否新增账号,自动配置同步账号
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
@ -199,22 +106,21 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
// 刷新页面UI
refreshUI();
}
/**
*
* 广
*/
@Override
protected void onDestroy() {
// 注销广播接收器,释放资源
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
/**
*
*
*
*/
private void loadAccountPreference() {
mAccountCategory.removeAll();
@ -223,24 +129,19 @@ public class NotesPreferenceActivity extends PreferenceActivity {
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
/**
*
*
*/
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// the first time to set account
// 首次设置账号,显示账号选择对话框
showSelectAccountAlertDialog();
} else {
// if the account has already been set, we need to promp
// user about the risk
// 已设置账号,显示更换账号确认对话框
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
@ -249,18 +150,15 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mAccountCategory.addPreference(accountPref);
}
/**
*
*
*
*/
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
/**
*
*
*/
// set button state
// 根据同步状态设置按钮样式和点击事件
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
@ -276,9 +174,10 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
});
}
// 未设置账号时禁用同步按钮
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// set last sync time
// 设置最后同步时间展示
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
@ -295,20 +194,21 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
/**
* UI+
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/**
*
* Google
* /
*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
/**
*
*
*/
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
@ -324,6 +224,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mOriAccounts = accounts;
mHasAddedAccount = false;
// 展示已有谷歌账号列表
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
@ -344,24 +245,18 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
});
}
/**
* "添加账户"
*
*/
// 添加新增账号视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
/**
*
* Gmail
*/
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
@ -369,9 +264,13 @@ public class NotesPreferenceActivity extends PreferenceActivity {
});
}
/**
* //
*/
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
@ -380,6 +279,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
// 对话框选项
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
@ -398,11 +298,19 @@ public class NotesPreferenceActivity extends PreferenceActivity {
dialogBuilder.show();
}
/**
*
* @return
*/
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
/**
*
* @param account
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
@ -414,10 +322,10 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
editor.commit();
// clean up last sync time
// 清空最后同步时间
setLastSyncTime(this, 0);
// clean up local gtask related info
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
@ -433,6 +341,9 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
/**
*
*/
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
@ -444,7 +355,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
editor.commit();
// clean up local gtask related info
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
@ -455,12 +366,22 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}).start();
}
/**
*
* @param context
* @return
*/
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
/**
*
* @param context
* @param time
*/
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
@ -469,12 +390,116 @@ public class NotesPreferenceActivity extends PreferenceActivity {
editor.commit();
}
/**
*
* @param context
* @return
*/
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
/**
*
*/
private void showPasswordSettingDialog() {
final boolean hasPassword = PasswordVerifyActivity.isPasswordEnabled(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(hasPassword ? "修改密码" : "设置密码");
// 布局包含密码输入框
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText passwordEditText = (EditText) view.findViewById(R.id.et_foler_name);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
passwordEditText.setHint(hasPassword ? "输入新密码" : "设置密码");
passwordEditText.setMaxLength(4);
builder.setView(view);
builder.setPositiveButton(hasPassword ? "修改" : "设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString().trim();
if (TextUtils.isEmpty(password) || password.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (hasPassword) {
// 修改密码,需要验证旧密码
showVerifyOldPasswordDialog(password);
} else {
// 设置新密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, password);
Toast.makeText(NotesPreferenceActivity.this, "密码设置成功", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
// 如果已有密码,添加关闭密码锁的选项
if (hasPassword) {
builder.setNeutralButton("关闭密码锁", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PasswordVerifyActivity.clearPassword(NotesPreferenceActivity.this);
Toast.makeText(NotesPreferenceActivity.this, "密码锁已关闭", Toast.LENGTH_SHORT).show();
}
});
}
builder.show();
}
/**
*
* @param newPassword
*/
private void showVerifyOldPasswordDialog(final String newPassword) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证旧密码");
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText oldPasswordEditText = (EditText) view.findViewById(R.id.et_foler_name);
oldPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
oldPasswordEditText.setHint("输入旧密码");
oldPasswordEditText.setMaxLength(4);
builder.setView(view);
builder.setPositiveButton("验证", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String oldPassword = oldPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(oldPassword) || oldPassword.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (oldPassword.equals(PasswordVerifyActivity.getSavedPassword(NotesPreferenceActivity.this))) {
// 旧密码正确,修改密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, newPassword);
Toast.makeText(NotesPreferenceActivity.this, "密码修改成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(NotesPreferenceActivity.this, "旧密码错误", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
/**
* 广
* 广UI
*/
private class GTaskReceiver extends BroadcastReceiver {
@Override
@ -485,13 +510,16 @@ public class NotesPreferenceActivity extends PreferenceActivity {
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
/**
* ActionBar
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// 返回笔记列表首页
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
@ -500,4 +528,4 @@ public class NotesPreferenceActivity extends PreferenceActivity {
return false;
}
}
}
}

@ -0,0 +1,532 @@
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
*
*
*/
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账号名称偏好键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 最后同步时间偏好键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色设置偏好键
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账号偏好分类键
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账号权限过滤键
private PreferenceCategory mAccountCategory; // 账号设置偏好分类
private GTaskReceiver mReceiver; // 同步状态广播接收器
private Account[] mOriAccounts; // 原始谷歌账号数组
private boolean mHasAddedAccount; // 是否新增账号标记
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 设置ActionBar返回按钮
getActionBar().setDisplayHomeAsUpEnabled(true);
// 加载偏好设置资源
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
// 注册同步服务广播过滤器
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
// 添加设置页面头部视图
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
// 为密码锁设置选项添加点击事件
Preference passwordSettingPref = findPreference("pref_key_password_setting");
if (passwordSettingPref != null) {
passwordSettingPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
showPasswordSettingDialog();
return true;
}
});
}
}
@Override
protected void onResume() {
super.onResume();
// 检测是否新增账号,自动配置同步账号
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
// 刷新页面UI
refreshUI();
}
@Override
protected void onDestroy() {
// 注销广播接收器,释放资源
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
/**
*
*/
private void loadAccountPreference() {
mAccountCategory.removeAll();
Preference accountPref = new Preference(this);
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// 首次设置账号,显示账号选择对话框
showSelectAccountAlertDialog();
} else {
// 已设置账号,显示更换账号确认对话框
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
mAccountCategory.addPreference(accountPref);
}
/**
*
*/
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 根据同步状态设置按钮样式和点击事件
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
// 未设置账号时禁用同步按钮
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// 设置最后同步时间展示
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
/**
* UI+
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/**
* /
*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
mHasAddedAccount = false;
// 展示已有谷歌账号列表
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
refreshUI();
}
});
}
// 添加新增账号视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
}
});
}
/**
* //
*/
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
// 对话框选项
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
removeSyncAccount();
refreshUI();
}
}
});
dialogBuilder.show();
}
/**
*
* @return
*/
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
/**
*
* @param account
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
editor.commit();
// 清空最后同步时间
setLastSyncTime(this, 0);
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}
}
/**
*
*/
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
editor.commit();
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
}
/**
*
* @param context
* @return
*/
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
/**
*
* @param context
* @param time
*/
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
}
/**
*
* @param context
* @return
*/
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
/**
*
*/
private void showPasswordSettingDialog() {
final boolean hasPassword = PasswordVerifyActivity.isPasswordEnabled(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(hasPassword ? "修改密码" : "设置密码");
// 布局包含密码输入框
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText passwordEditText = (EditText) view.findViewById(R.id.et_foler_name);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
passwordEditText.setHint(hasPassword ? "输入新密码" : "设置密码");
passwordEditText.setMaxLength(4);
builder.setView(view);
builder.setPositiveButton(hasPassword ? "修改" : "设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString().trim();
if (TextUtils.isEmpty(password) || password.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (hasPassword) {
// 修改密码,需要验证旧密码
showVerifyOldPasswordDialog(password);
} else {
// 设置新密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, password);
Toast.makeText(NotesPreferenceActivity.this, "密码设置成功", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
// 如果已有密码,添加关闭密码锁的选项
if (hasPassword) {
builder.setNeutralButton("关闭密码锁", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PasswordVerifyActivity.clearPassword(NotesPreferenceActivity.this);
Toast.makeText(NotesPreferenceActivity.this, "密码锁已关闭", Toast.LENGTH_SHORT).show();
}
});
}
builder.show();
}
/**
*
* @param newPassword
*/
private void showVerifyOldPasswordDialog(final String newPassword) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证旧密码");
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText oldPasswordEditText = (EditText) view.findViewById(R.id.et_foler_name);
oldPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
oldPasswordEditText.setHint("输入旧密码");
oldPasswordEditText.setMaxLength(4);
builder.setView(view);
builder.setPositiveButton("验证", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String oldPassword = oldPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(oldPassword) || oldPassword.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (oldPassword.equals(PasswordVerifyActivity.getSavedPassword(NotesPreferenceActivity.this))) {
// 旧密码正确,修改密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, newPassword);
Toast.makeText(NotesPreferenceActivity.this, "密码修改成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(NotesPreferenceActivity.this, "旧密码错误", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
/**
* 广
* 广UI
*/
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
/**
* ActionBar
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// 返回笔记列表首页
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}

@ -0,0 +1,532 @@
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
*
*
*/
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账号名称偏好键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 最后同步时间偏好键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色设置偏好键
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账号偏好分类键
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账号权限过滤键
private PreferenceCategory mAccountCategory; // 账号设置偏好分类
private GTaskReceiver mReceiver; // 同步状态广播接收器
private Account[] mOriAccounts; // 原始谷歌账号数组
private boolean mHasAddedAccount; // 是否新增账号标记
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 设置ActionBar返回按钮
getActionBar().setDisplayHomeAsUpEnabled(true);
// 加载偏好设置资源
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
// 注册同步服务广播过滤器
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
// 添加设置页面头部视图
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
// 为密码锁设置选项添加点击事件
Preference passwordSettingPref = findPreference("pref_key_password_setting");
if (passwordSettingPref != null) {
passwordSettingPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
showPasswordSettingDialog();
return true;
}
});
}
}
@Override
protected void onResume() {
super.onResume();
// 检测是否新增账号,自动配置同步账号
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
// 刷新页面UI
refreshUI();
}
@Override
protected void onDestroy() {
// 注销广播接收器,释放资源
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
/**
*
*/
private void loadAccountPreference() {
mAccountCategory.removeAll();
Preference accountPref = new Preference(this);
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// 首次设置账号,显示账号选择对话框
showSelectAccountAlertDialog();
} else {
// 已设置账号,显示更换账号确认对话框
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
mAccountCategory.addPreference(accountPref);
}
/**
*
*/
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 根据同步状态设置按钮样式和点击事件
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
// 未设置账号时禁用同步按钮
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// 设置最后同步时间展示
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
/**
* UI+
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/**
* /
*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
mHasAddedAccount = false;
// 展示已有谷歌账号列表
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
refreshUI();
}
});
}
// 添加新增账号视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
}
});
}
/**
* //
*/
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
// 对话框选项
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
removeSyncAccount();
refreshUI();
}
}
});
dialogBuilder.show();
}
/**
*
* @return
*/
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
/**
*
* @param account
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
editor.commit();
// 清空最后同步时间
setLastSyncTime(this, 0);
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}
}
/**
*
*/
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
editor.commit();
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
}
/**
*
* @param context
* @return
*/
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
/**
*
* @param context
* @param time
*/
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
}
/**
*
* @param context
* @return
*/
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
/**
*
*/
private void showPasswordSettingDialog() {
final boolean hasPassword = PasswordVerifyActivity.isPasswordEnabled(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(hasPassword ? "修改密码" : "设置密码");
// 布局包含密码输入框
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText passwordEditText = (EditText) view.findViewById(R.id.et_foler_name);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
passwordEditText.setHint(hasPassword ? "输入新密码" : "设置密码");
passwordEditText.setFilters(new android.text.InputFilter[] {new android.text.InputFilter.LengthFilter(4)});
builder.setView(view);
builder.setPositiveButton(hasPassword ? "修改" : "设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString().trim();
if (TextUtils.isEmpty(password) || password.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (hasPassword) {
// 修改密码,需要验证旧密码
showVerifyOldPasswordDialog(password);
} else {
// 设置新密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, password);
Toast.makeText(NotesPreferenceActivity.this, "密码设置成功", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
// 如果已有密码,添加关闭密码锁的选项
if (hasPassword) {
builder.setNeutralButton("关闭密码锁", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PasswordVerifyActivity.clearPassword(NotesPreferenceActivity.this);
Toast.makeText(NotesPreferenceActivity.this, "密码锁已关闭", Toast.LENGTH_SHORT).show();
}
});
}
builder.show();
}
/**
*
* @param newPassword
*/
private void showVerifyOldPasswordDialog(final String newPassword) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证旧密码");
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText oldPasswordEditText = (EditText) view.findViewById(R.id.et_foler_name);
oldPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
oldPasswordEditText.setHint("输入旧密码");
oldPasswordEditText.setMaxLength(4);
builder.setView(view);
builder.setPositiveButton("验证", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String oldPassword = oldPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(oldPassword) || oldPassword.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (oldPassword.equals(PasswordVerifyActivity.getSavedPassword(NotesPreferenceActivity.this))) {
// 旧密码正确,修改密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, newPassword);
Toast.makeText(NotesPreferenceActivity.this, "密码修改成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(NotesPreferenceActivity.this, "旧密码错误", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
/**
* 广
* 广UI
*/
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
/**
* ActionBar
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// 返回笔记列表首页
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}

@ -0,0 +1,532 @@
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
*
*
*/
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账号名称偏好键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 最后同步时间偏好键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色设置偏好键
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账号偏好分类键
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账号权限过滤键
private PreferenceCategory mAccountCategory; // 账号设置偏好分类
private GTaskReceiver mReceiver; // 同步状态广播接收器
private Account[] mOriAccounts; // 原始谷歌账号数组
private boolean mHasAddedAccount; // 是否新增账号标记
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 设置ActionBar返回按钮
getActionBar().setDisplayHomeAsUpEnabled(true);
// 加载偏好设置资源
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
// 注册同步服务广播过滤器
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
// 添加设置页面头部视图
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
// 为密码锁设置选项添加点击事件
Preference passwordSettingPref = findPreference("pref_key_password_setting");
if (passwordSettingPref != null) {
passwordSettingPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
showPasswordSettingDialog();
return true;
}
});
}
}
@Override
protected void onResume() {
super.onResume();
// 检测是否新增账号,自动配置同步账号
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
// 刷新页面UI
refreshUI();
}
@Override
protected void onDestroy() {
// 注销广播接收器,释放资源
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
/**
*
*/
private void loadAccountPreference() {
mAccountCategory.removeAll();
Preference accountPref = new Preference(this);
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// 首次设置账号,显示账号选择对话框
showSelectAccountAlertDialog();
} else {
// 已设置账号,显示更换账号确认对话框
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
mAccountCategory.addPreference(accountPref);
}
/**
*
*/
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 根据同步状态设置按钮样式和点击事件
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
// 未设置账号时禁用同步按钮
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// 设置最后同步时间展示
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
/**
* UI+
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/**
* /
*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
mHasAddedAccount = false;
// 展示已有谷歌账号列表
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
refreshUI();
}
});
}
// 添加新增账号视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
}
});
}
/**
* //
*/
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
// 对话框选项
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
removeSyncAccount();
refreshUI();
}
}
});
dialogBuilder.show();
}
/**
*
* @return
*/
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
/**
*
* @param account
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
editor.commit();
// 清空最后同步时间
setLastSyncTime(this, 0);
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}
}
/**
*
*/
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
editor.commit();
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
}
/**
*
* @param context
* @return
*/
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
/**
*
* @param context
* @param time
*/
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
}
/**
*
* @param context
* @return
*/
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
/**
*
*/
private void showPasswordSettingDialog() {
final boolean hasPassword = PasswordVerifyActivity.isPasswordEnabled(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(hasPassword ? "修改密码" : "设置密码");
// 布局包含密码输入框
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText passwordEditText = (EditText) view.findViewById(R.id.et_foler_name);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
passwordEditText.setHint(hasPassword ? "输入新密码" : "设置密码");
passwordEditText.setFilters(new android.text.InputFilter[] {new android.text.InputFilter.LengthFilter(4)});
builder.setView(view);
builder.setPositiveButton(hasPassword ? "修改" : "设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString().trim();
if (TextUtils.isEmpty(password) || password.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (hasPassword) {
// 修改密码,需要验证旧密码
showVerifyOldPasswordDialog(password);
} else {
// 设置新密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, password);
Toast.makeText(NotesPreferenceActivity.this, "密码设置成功", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
// 如果已有密码,添加关闭密码锁的选项
if (hasPassword) {
builder.setNeutralButton("关闭密码锁", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PasswordVerifyActivity.clearPassword(NotesPreferenceActivity.this);
Toast.makeText(NotesPreferenceActivity.this, "密码锁已关闭", Toast.LENGTH_SHORT).show();
}
});
}
builder.show();
}
/**
*
* @param newPassword
*/
private void showVerifyOldPasswordDialog(final String newPassword) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证旧密码");
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText oldPasswordEditText = (EditText) view.findViewById(R.id.et_foler_name);
oldPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
oldPasswordEditText.setHint("输入旧密码");
oldPasswordEditText.setFilters(new android.text.InputFilter[] {new android.text.InputFilter.LengthFilter(4)});
builder.setView(view);
builder.setPositiveButton("验证", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String oldPassword = oldPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(oldPassword) || oldPassword.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (oldPassword.equals(PasswordVerifyActivity.getSavedPassword(NotesPreferenceActivity.this))) {
// 旧密码正确,修改密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, newPassword);
Toast.makeText(NotesPreferenceActivity.this, "密码修改成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(NotesPreferenceActivity.this, "旧密码错误", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
/**
* 广
* 广UI
*/
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
/**
* ActionBar
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// 返回笔记列表首页
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}

@ -0,0 +1,532 @@
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
*
*
*/
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账号名称偏好键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 最后同步时间偏好键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色设置偏好键
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账号偏好分类键
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账号权限过滤键
private PreferenceCategory mAccountCategory; // 账号设置偏好分类
private GTaskReceiver mReceiver; // 同步状态广播接收器
private Account[] mOriAccounts; // 原始谷歌账号数组
private boolean mHasAddedAccount; // 是否新增账号标记
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 设置ActionBar返回按钮
getActionBar().setDisplayHomeAsUpEnabled(true);
// 加载偏好设置资源
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
// 注册同步服务广播过滤器
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
// 添加设置页面头部视图
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
// 为密码锁设置选项添加点击事件
Preference passwordSettingPref = findPreference("pref_key_password_setting");
if (passwordSettingPref != null) {
passwordSettingPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
showPasswordSettingDialog();
return true;
}
});
}
}
@Override
protected void onResume() {
super.onResume();
// 检测是否新增账号,自动配置同步账号
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
// 刷新页面UI
refreshUI();
}
@Override
protected void onDestroy() {
// 注销广播接收器,释放资源
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
/**
*
*/
private void loadAccountPreference() {
mAccountCategory.removeAll();
Preference accountPref = new Preference(this);
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// 首次设置账号,显示账号选择对话框
showSelectAccountAlertDialog();
} else {
// 已设置账号,显示更换账号确认对话框
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
mAccountCategory.addPreference(accountPref);
}
/**
*
*/
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 根据同步状态设置按钮样式和点击事件
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
// 未设置账号时禁用同步按钮
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// 设置最后同步时间展示
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
/**
* UI+
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/**
* /
*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
mHasAddedAccount = false;
// 展示已有谷歌账号列表
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
refreshUI();
}
});
}
// 添加新增账号视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
}
});
}
/**
* //
*/
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 设置自定义对话框标题
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
// 对话框选项
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
removeSyncAccount();
refreshUI();
}
}
});
dialogBuilder.show();
}
/**
*
* @return
*/
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
/**
*
* @param account
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
editor.commit();
// 清空最后同步时间
setLastSyncTime(this, 0);
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}
}
/**
*
*/
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
editor.commit();
// 异步清理本地同步相关数据
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
}
/**
*
* @param context
* @return
*/
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
/**
*
* @param context
* @param time
*/
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
}
/**
*
* @param context
* @return
*/
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
/**
*
*/
private void showPasswordSettingDialog() {
final boolean hasPassword = PasswordVerifyActivity.isPasswordEnabled(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(hasPassword ? "修改密码" : "设置密码");
// 布局包含密码输入框
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText passwordEditText = (EditText) view.findViewById(R.id.et_foler_name);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
passwordEditText.setHint(hasPassword ? "输入新密码" : "设置密码");
passwordEditText.setFilters(new android.text.InputFilter[] {new android.text.InputFilter.LengthFilter(4)});
builder.setView(view);
builder.setPositiveButton(hasPassword ? "修改" : "设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString().trim();
if (TextUtils.isEmpty(password) || password.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (hasPassword) {
// 修改密码,需要验证旧密码
showVerifyOldPasswordDialog(password);
} else {
// 设置新密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, password);
Toast.makeText(NotesPreferenceActivity.this, "密码设置成功", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
// 如果已有密码,添加关闭密码锁的选项
if (hasPassword) {
builder.setNeutralButton("关闭密码锁", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PasswordVerifyActivity.clearPassword(NotesPreferenceActivity.this);
Toast.makeText(NotesPreferenceActivity.this, "密码锁已关闭", Toast.LENGTH_SHORT).show();
}
});
}
builder.show();
}
/**
*
* @param newPassword
*/
private void showVerifyOldPasswordDialog(final String newPassword) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证旧密码");
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_edit_text, null);
final EditText oldPasswordEditText = (EditText) view.findViewById(R.id.et_foler_name);
oldPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
oldPasswordEditText.setHint("输入旧密码");
oldPasswordEditText.setFilters(new android.text.InputFilter[] {new android.text.InputFilter.LengthFilter(4)});
builder.setView(view);
builder.setPositiveButton("验证", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String oldPassword = oldPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(oldPassword) || oldPassword.length() != 4) {
Toast.makeText(NotesPreferenceActivity.this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
if (oldPassword.equals(PasswordVerifyActivity.getSavedPassword(NotesPreferenceActivity.this))) {
// 旧密码正确,修改密码
PasswordVerifyActivity.setPassword(NotesPreferenceActivity.this, newPassword);
Toast.makeText(NotesPreferenceActivity.this, "密码修改成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(NotesPreferenceActivity.this, "旧密码错误", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
/**
* 广
* 广UI
*/
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
/**
* ActionBar
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// 返回笔记列表首页
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}

@ -0,0 +1,143 @@
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
public class PasswordVerifyActivity extends Activity implements OnClickListener {
private static final String PREFERENCE_PASSWORD_ENABLED = "net.micode.notes.password_enabled";
private static final String PREFERENCE_PASSWORD = "net.micode.notes.password";
private EditText mPasswordEditText;
private Button mVerifyButton;
private Button mForgotPasswordButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_password_verify);
initViews();
}
private void initViews() {
mPasswordEditText = (EditText) findViewById(R.id.et_password);
mVerifyButton = (Button) findViewById(R.id.btn_verify);
mForgotPasswordButton = (Button) findViewById(R.id.btn_forgot_password);
mVerifyButton.setOnClickListener(this);
mForgotPasswordButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_verify:
verifyPassword();
break;
case R.id.btn_forgot_password:
showForgotPasswordDialog();
break;
default:
break;
}
}
private void verifyPassword() {
String inputPassword = mPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(inputPassword)) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
if (inputPassword.length() != 4) {
Toast.makeText(this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
String savedPassword = getSavedPassword();
if (inputPassword.equals(savedPassword)) {
// 密码正确,进入应用
Intent intent = new Intent(this, NotesListActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(this, "密码错误,请重新输入", Toast.LENGTH_SHORT).show();
mPasswordEditText.setText("");
}
}
private void showForgotPasswordDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("忘记密码")
.setMessage("确定要重置密码吗?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
resetPassword();
}
})
.setNegativeButton("取消", null)
.show();
}
private void resetPassword() {
// 重置密码
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, false)
.putString(PREFERENCE_PASSWORD, "")
.apply();
Toast.makeText(this, "密码已重置", Toast.LENGTH_SHORT).show();
// 进入应用
Intent intent = new Intent(this, NotesListActivity.class);
startActivity(intent);
finish();
}
// 静态方法:判断密码锁是否已启用
public static boolean isPasswordEnabled(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getBoolean(PREFERENCE_PASSWORD_ENABLED, false);
}
// 静态方法:获取保存的密码
public static String getSavedPassword(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(PREFERENCE_PASSWORD, "");
}
// 静态方法:设置密码
public static void setPassword(Context context, String password) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, true)
.putString(PREFERENCE_PASSWORD, password)
.apply();
}
// 静态方法:清除密码
public static void clearPassword(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, false)
.putString(PREFERENCE_PASSWORD, "")
.apply();
}
}

@ -0,0 +1,143 @@
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
public class PasswordVerifyActivity extends Activity implements OnClickListener {
private static final String PREFERENCE_PASSWORD_ENABLED = "net.micode.notes.password_enabled";
private static final String PREFERENCE_PASSWORD = "net.micode.notes.password";
private EditText mPasswordEditText;
private Button mVerifyButton;
private Button mForgotPasswordButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_password_verify);
initViews();
}
private void initViews() {
mPasswordEditText = (EditText) findViewById(R.id.et_password);
mVerifyButton = (Button) findViewById(R.id.btn_verify);
mForgotPasswordButton = (Button) findViewById(R.id.btn_forgot_password);
mVerifyButton.setOnClickListener(this);
mForgotPasswordButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_verify:
verifyPassword();
break;
case R.id.btn_forgot_password:
showForgotPasswordDialog();
break;
default:
break;
}
}
private void verifyPassword() {
String inputPassword = mPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(inputPassword)) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
if (inputPassword.length() != 4) {
Toast.makeText(this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
String savedPassword = getSavedPassword(this);
if (inputPassword.equals(savedPassword)) {
// 密码正确,进入应用
Intent intent = new Intent(this, NotesListActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(this, "密码错误,请重新输入", Toast.LENGTH_SHORT).show();
mPasswordEditText.setText("");
}
}
private void showForgotPasswordDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("忘记密码")
.setMessage("确定要重置密码吗?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
resetPassword();
}
})
.setNegativeButton("取消", null)
.show();
}
private void resetPassword() {
// 重置密码
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, false)
.putString(PREFERENCE_PASSWORD, "")
.apply();
Toast.makeText(this, "密码已重置", Toast.LENGTH_SHORT).show();
// 进入应用
Intent intent = new Intent(this, NotesListActivity.class);
startActivity(intent);
finish();
}
// 静态方法:判断密码锁是否已启用
public static boolean isPasswordEnabled(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getBoolean(PREFERENCE_PASSWORD_ENABLED, false);
}
// 静态方法:获取保存的密码
public static String getSavedPassword(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(PREFERENCE_PASSWORD, "");
}
// 静态方法:设置密码
public static void setPassword(Context context, String password) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, true)
.putString(PREFERENCE_PASSWORD, password)
.apply();
}
// 静态方法:清除密码
public static void clearPassword(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, false)
.putString(PREFERENCE_PASSWORD, "")
.apply();
}
}

@ -0,0 +1,143 @@
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
public class PasswordVerifyActivity extends Activity implements OnClickListener {
private static final String PREFERENCE_PASSWORD_ENABLED = "net.micode.notes.password_enabled";
private static final String PREFERENCE_PASSWORD = "net.micode.notes.password";
private EditText mPasswordEditText;
private Button mVerifyButton;
private Button mForgotPasswordButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_password_verify);
initViews();
}
private void initViews() {
mPasswordEditText = (EditText) findViewById(R.id.et_password);
mVerifyButton = (Button) findViewById(R.id.btn_verify);
mForgotPasswordButton = (Button) findViewById(R.id.btn_forgot_password);
mVerifyButton.setOnClickListener(this);
mForgotPasswordButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_verify:
verifyPassword();
break;
case R.id.btn_forgot_password:
showForgotPasswordDialog();
break;
default:
break;
}
}
private void verifyPassword() {
String inputPassword = mPasswordEditText.getText().toString().trim();
if (TextUtils.isEmpty(inputPassword)) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
if (inputPassword.length() != 4) {
Toast.makeText(this, "请输入4位数字密码", Toast.LENGTH_SHORT).show();
return;
}
String savedPassword = getSavedPassword(this);
if (inputPassword.equals(savedPassword)) {
// 密码正确,进入应用
Intent intent = new Intent(this, NotesListActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(this, "密码错误,请重新输入", Toast.LENGTH_SHORT).show();
mPasswordEditText.setText("");
}
}
private void showForgotPasswordDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("忘记密码")
.setMessage("确定要重置密码吗?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
resetPassword();
}
})
.setNegativeButton("取消", null)
.show();
}
private void resetPassword() {
// 重置密码
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, false)
.putString(PREFERENCE_PASSWORD, "")
.apply();
Toast.makeText(this, "密码已重置", Toast.LENGTH_SHORT).show();
// 进入应用
Intent intent = new Intent(this, NotesListActivity.class);
startActivity(intent);
finish();
}
// 静态方法:判断密码锁是否已启用
public static boolean isPasswordEnabled(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getBoolean(PREFERENCE_PASSWORD_ENABLED, false);
}
// 静态方法:获取保存的密码
public static String getSavedPassword(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(PREFERENCE_PASSWORD, "");
}
// 静态方法:设置密码
public static void setPassword(Context context, String password) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, true)
.putString(PREFERENCE_PASSWORD, password)
.apply();
}
// 静态方法:清除密码
public static void clearPassword(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit()
.putBoolean(PREFERENCE_PASSWORD_ENABLED, false)
.putString(PREFERENCE_PASSWORD, "")
.apply();
}
}

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/list_background"
android:orientation="vertical"
android:padding="20dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="60dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="@string/app_name"
android:textSize="24sp"
android:textColor="@color/primary_text_dark" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请输入密码"
android:textSize="18sp"
android:textColor="@color/primary_text_dark" />
<EditText
android:id="@+id/et_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:inputType="numberPassword"
android:background="@android:drawable/edit_text"
android:hint="输入4位数字密码"
android:maxLength="4"
android:gravity="center"
android:textSize="24sp" />
<Button
android:id="@+id/btn_verify"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="验证"
android:textSize="18sp" />
<Button
android:id="@+id/btn_forgot_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="忘记密码?"
android:textSize="14sp"
android:background="@null"
android:textColor="@color/secondary_text_dark" />
</LinearLayout>
</LinearLayout>

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/list_background"
android:orientation="vertical"
android:padding="20dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="60dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="@string/app_name"
android:textSize="24sp"
android:textColor="@color/primary_text_dark" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请输入密码"
android:textSize="18sp"
android:textColor="@color/primary_text_dark" />
<EditText
android:id="@+id/et_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:inputType="numberPassword"
android:background="@android:drawable/edit_text"
android:hint="输入4位数字密码"
android:maxLength="4"
android:gravity="center"
android:textSize="24sp" />
<Button
android:id="@+id/btn_verify"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="验证"
android:textSize="18sp" />
<Button
android:id="@+id/btn_forgot_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="忘记密码?"
android:textSize="14sp"
android:background="@null"
android:textColor="@color/secondary_text_dark" />
</LinearLayout>
</LinearLayout>

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/list_background"
android:orientation="vertical"
android:padding="20dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="60dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="@string/app_name"
android:textSize="24sp"
android:textColor="@color/primary_text_dark" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请输入密码"
android:textSize="18sp"
android:textColor="@color/primary_text_dark" />
<EditText
android:id="@+id/et_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:inputType="numberPassword"
android:background="@android:drawable/edit_text"
android:hint="输入4位数字密码"
android:maxLength="4"
android:gravity="center"
android:textSize="24sp" />
<Button
android:id="@+id/btn_verify"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="验证"
android:textSize="18sp" />
<Button
android:id="@+id/btn_forgot_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="忘记密码?"
android:textSize="14sp"
android:background="@null"
android:textColor="@color/secondary_text_dark" />
</LinearLayout>
</LinearLayout>

@ -27,4 +27,12 @@
android:title="@string/preferences_bg_random_appear_title"
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="密码锁设置">
<Preference
android:key="pref_key_password_setting"
android:title="密码锁设置"
android:summary="设置或修改密码" />
</PreferenceCategory>
</PreferenceScreen>

@ -27,4 +27,12 @@
android:title="@string/preferences_bg_random_appear_title"
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="密码锁设置">
<Preference
android:key="pref_key_password_setting"
android:title="密码锁设置"
android:summary="设置或修改密码" />
</PreferenceCategory>
</PreferenceScreen>

@ -15,25 +15,24 @@
limitations under the License.
-->
<menu
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_new_folder"
android:title="@string/menu_create_folder"/>
<item
android:id="@+id/menu_export_text"
android:title="@string/menu_export_text"/>
<item
android:id="@+id/menu_sync"
android:title="@string/menu_sync"/>
<item
android:id="@+id/menu_setting"
android:title="@string/menu_setting" />
<item
android:id="@+id/menu_search"
android:title="@string/menu_search"/>
</menu>
<PreferenceCategory
android:key="pref_sync_account_key">
</PreferenceCategory>
<PreferenceCategory>
<CheckBoxPreference
android:key="pref_key_bg_random_appear"
android:title="@string/preferences_bg_random_appear_title"
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="密码锁设置">
<Preference
android:key="pref_key_password_setting"
android:title="密码锁设置"
android:summary="设置或修改密码" />
</PreferenceCategory>
</PreferenceScreen>

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA" serialisationVersion="2">
<checkstyleVersion>12.3.1</checkstyleVersion>
<scanScope>JavaOnly</scanScope>
<copyLibs>true</copyLibs>
<option name="thirdPartyClasspath" />
<option name="activeLocationIds" />
<option name="locations">
<list>
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
</list>
</option>
</component>
</project>

@ -4,6 +4,14 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-01-13T14:37:17.057329800Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\fancanyu\.android\avd\Pixel_5.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>

@ -3,6 +3,10 @@
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Status" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SonarLintProjectSettings">
<option name="bindingEnabled" value="true" />
<option name="projectKey" value="Test" />
<option name="serverId" value="LocalSonar" />
</component>
</project>

@ -4,6 +4,153 @@
<shared>
<config />
</shared>
<layouts>
<layout url="file://$PROJECT_DIR$/app/src/main/res/drawable/ic_launcher_background.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/drawable/ic_launcher_foreground.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/drawable/new_note.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/account_dialog_title.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/activity_main.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/activity_password_verify.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/add_account_text.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/datetime_picker.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/dialog_edit_text.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/folder_list_item.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/note_edit.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/note_edit_list_item.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/note_item.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/note_list.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/note_list_dropdown_menu.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/note_list_footer.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/settings_header.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/widget_2x.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/layout/widget_4x.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/menu/call_note_edit.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/menu/call_record_folder.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/menu/note_edit.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/menu/note_list.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/menu/note_list_dropdown.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/menu/note_list_options.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/menu/sub_folder.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/mipmap-anydpi/ic_launcher.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
<layout url="file://$PROJECT_DIR$/app/src/main/res/xml/preferences.xml">
<config>
<theme>@style/Theme.Notesmaster</theme>
</config>
</layout>
</layouts>
</component>
<component name="AutoImportSettings">
<option name="autoReloadType" value="NONE" />
@ -18,7 +165,7 @@
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[DeviceId(pluginId=LocalEmulator, isTemplate=false, identifier=path=C:\Users\fancanyu\.android\avd\Pixel_5.avd)]" />
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[DeviceId(pluginId=LocalEmulator, isTemplate=false, identifier=path=C:\Users\fancanyu\.android\avd\Pixel_7.avd)]" />
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
@ -54,11 +201,15 @@
&quot;keyToString&quot;: {
&quot;Android App.app.executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;ResourceManagerPrefKey.ResourceType&quot;: &quot;DRAWABLE&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;RunOnceActivity.readMode.enableVisualFormatting&quot;: &quot;true&quot;,
&quot;cf.first.check.clang-format&quot;: &quot;false&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;com.android.tools.idea.streaming.zoom.toolbar.visible&quot;: &quot;false&quot;,
&quot;com.google.services.firebase.aqiPopupShown&quot;: &quot;true&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Projects/Notes-master/app/src/main/res&quot;,
&quot;project.structure.last.edited&quot;: &quot;Dependencies&quot;,
@ -73,6 +224,9 @@
<recent name="D:\Projects\Notes-master\app\src\main\res" />
<recent name="D:\Projects\Notes-master\app\src\main\java\net\micode\notes" />
</key>
<key name="CopyClassDialog.RECENTS_KEY">
<recent name="net.micode.notes.data" />
</key>
</component>
<component name="RunManager">
<configuration name="app" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
@ -158,17 +312,28 @@
</task>
<servers />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/app/src/main/java/net/micode/notes/data/NotesProvider.java</url>
<line>293</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="play_dynamic_filters_status">
<option name="appIdToCheckInfo">
<map>
<entry key="net.micode.notes">
<value>
<CheckInfo lastCheckTimestamp="1763812814201" />
<CheckInfo lastCheckTimestamp="1768825900066" />
</value>
</entry>
<entry key="net.micode.notes.test">
<value>
<CheckInfo lastCheckTimestamp="1763812940491" />
<CheckInfo lastCheckTimestamp="1768825900071" />
</value>
</entry>
</map>

@ -1,190 +0,0 @@
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.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

@ -1,23 +0,0 @@
[中文]
1. MiCode便签是小米便签的社区开源版由MIUI团队(www.miui.com) 发起并贡献第一批代码遵循NOTICE文件所描述的开源协议
今后为MiCode社区(www.micode.net) 拥有,并由社区发布和维护。
2. Bug反馈和跟踪请访问Github,
https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open
3. 功能建议和综合讨论请访问MiCode,
http://micode.net/forum.php?mod=forumdisplay&fid=38
[English]
1. MiCode Notes is open source edition of XM notepad, it's first initiated and sponsored by MIUI team (www.miui.com).
It's opened under license described by NOTICE file. It's owned by the MiCode community (www.micode.net). In future,
the MiCode community will release and maintain this project.
2. Regarding issue tracking, please visit Github,
https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open
3. Regarding feature request and general discussion, please visit Micode forum,
http://micode.net/forum.php?mod=forumdisplay&fid=38

@ -1 +1,2 @@
/build
/build
app/build/

@ -4,7 +4,7 @@ plugins {
android {
namespace = "net.micode.notes"
compileSdk {
compileSdk{
version = release(36)
}

@ -1,4 +1,4 @@
#Sat Nov 22 20:02:27 CST 2025
#Sun Jan 18 20:02:40 CST 2026
net.micode.notes.app-main-33\:/color/primary_text_dark.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\color_primary_text_dark.xml.flat
net.micode.notes.app-main-33\:/color/secondary_text_dark.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\color_secondary_text_dark.xml.flat
net.micode.notes.app-main-33\:/drawable-hdpi/bg_btn_set_color.png=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\drawable-hdpi_bg_btn_set_color.png.flat
@ -71,6 +71,7 @@ net.micode.notes.app-main-33\:/drawable/ic_launcher_foreground.xml=D\:\\Projects
net.micode.notes.app-main-33\:/drawable/new_note.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\drawable_new_note.xml.flat
net.micode.notes.app-main-33\:/layout/account_dialog_title.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\layout_account_dialog_title.xml.flat
net.micode.notes.app-main-33\:/layout/activity_main.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\layout_activity_main.xml.flat
net.micode.notes.app-main-33\:/layout/activity_password_verify.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\layout_activity_password_verify.xml.flat
net.micode.notes.app-main-33\:/layout/add_account_text.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\layout_add_account_text.xml.flat
net.micode.notes.app-main-33\:/layout/datetime_picker.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\layout_datetime_picker.xml.flat
net.micode.notes.app-main-33\:/layout/dialog_edit_text.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\merged_res\\debug\\mergeDebugResources\\layout_dialog_edit_text.xml.flat

@ -2085,6 +2085,7 @@
<string name="menu_font_size">Font size</string>
<string name="menu_font_small">Small</string>
<string name="menu_font_super">Super</string>
<string name="menu_insert_image">Insert image</string>
<string name="menu_list_mode">Enter check list</string>
<string name="menu_move">Move to folder</string>
<string name="menu_move_parent_folder">Parent folder</string>

@ -1,4 +1,4 @@
#Sat Nov 22 20:02:27 CST 2025
#Sun Jan 18 20:02:40 CST 2026
net.micode.notes.app-main-5\:/color/primary_text_dark.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\color\\primary_text_dark.xml
net.micode.notes.app-main-5\:/color/secondary_text_dark.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\color\\secondary_text_dark.xml
net.micode.notes.app-main-5\:/drawable-hdpi/bg_btn_set_color.png=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\drawable-hdpi-v4\\bg_btn_set_color.png
@ -71,6 +71,7 @@ net.micode.notes.app-main-5\:/drawable/ic_launcher_foreground.xml=D\:\\Projects\
net.micode.notes.app-main-5\:/drawable/new_note.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\drawable\\new_note.xml
net.micode.notes.app-main-5\:/layout/account_dialog_title.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\layout\\account_dialog_title.xml
net.micode.notes.app-main-5\:/layout/activity_main.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\layout\\activity_main.xml
net.micode.notes.app-main-5\:/layout/activity_password_verify.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\layout\\activity_password_verify.xml
net.micode.notes.app-main-5\:/layout/add_account_text.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\layout\\add_account_text.xml
net.micode.notes.app-main-5\:/layout/datetime_picker.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\layout\\datetime_picker.xml
net.micode.notes.app-main-5\:/layout/dialog_edit_text.xml=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\packaged_res\\debug\\packageDebugResources\\layout\\dialog_edit_text.xml

@ -65,6 +65,7 @@
<string name="menu_font_size">Font size</string>
<string name="menu_font_small">Small</string>
<string name="menu_font_super">Super</string>
<string name="menu_insert_image">Insert image</string>
<string name="menu_list_mode">Enter check list</string>
<string name="menu_move">Move to folder</string>
<string name="menu_move_parent_folder">Parent folder</string>

@ -1,4 +1,4 @@
#Sat Nov 22 20:02:28 CST 2025
#Mon Jan 19 20:21:19 CST 2026
base.0=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\dex\\debug\\mergeExtDexDebug\\classes.dex
base.1=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\dex\\debug\\mergeProjectDexDebug\\0\\classes.dex
base.2=D\:\\Projects\\Notes-master\\app\\build\\intermediates\\dex\\debug\\mergeProjectDexDebug\\11\\classes.dex

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save