Compare commits

..

9 Commits

@ -0,0 +1 @@
Subproject commit 4f9b53791b00b089343a4919af9ad7ac765b751f

@ -0,0 +1,980 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
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;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.BackupUtils;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
//NoteListActivity类主要实现小米便签刚进入界面。该类为一种main Activityxml中有定义覆盖点击事件监听及长按事件监听类。。
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 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;
//创建Activity启动其生命周期。完成设置界面样式、初始化资源、且在用户第一次使用App时进行介绍
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_list);
initResources();
/**
* Insert an introduction when user firstly use this application
*/
setAppInfoFromRawRes();
}
//intent返回数据接收函数。形参为请求码、结果码、其他intent返回的数据。主要与NoteEditActivity交互
@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);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
//第一次使用App时调用显示introduction。
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);
if (in != null) {
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
char [] buf = new char[1024];
int len = 0;
while ((len = br.read(buf)) > 0) {
sb.append(buf, 0, len);
}
} else {
Log.e(TAG, "Read introduction file error");
return;
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.RED);
note.setWorkingText(sb.toString());
if (note.saveNote()) {
sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit();
} else {
Log.e(TAG, "Save introduction note error");
return;
}
}
}
//onCreate调用后回调此Activity对用户可见。
@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());
mNotesListView.setOnItemLongClickListener(this);
mNotesListAdapter = new NotesListAdapter(this);
mNotesListView.setAdapter(mNotesListAdapter);
mAddNewNote = (Button) findViewById(R.id.btn_new_note);
mAddNewNote.setOnClickListener(this);
mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener());
mDispatch = false;
mDispatchY = 0;
mOriginY = 0;
mTitleBar = (TextView) findViewById(R.id.tv_title_bar);
mState = ListEditState.NOTE_LIST;
mModeCallBack = new ModeCallback();
}
//ModeCallBack类定义了ActionMode的使用。便签长按事件通过ActionMode执行。
//便签长按后出现选择多个便签删除的菜单栏其在ActionBar中显示。
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu;
private ActionMode mActionMode;
private MenuItem mMoveMenu;
//ActionMode创建函数形参为创建的ActionMode及需要通过该ActionMode显示的Menu对象
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);
} else {
mMoveMenu.setVisible(true);
mMoveMenu.setOnMenuItemClickListener(this);
}
mActionMode = mode;
mNotesListAdapter.setChoiceMode(true);
mNotesListView.setLongClickable(false);
mAddNewNote.setVisibility(View.GONE);
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
R.layout.note_list_dropdown_menu, null);
mode.setCustomView(customView);
mDropDownMenu = new DropdownMenu(NotesListActivity.this,
(Button) customView.findViewById(R.id.selection_menu),
R.menu.note_list_dropdown);
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
public boolean onMenuItemClick(MenuItem item) {
mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());
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);
if (item != null) {
if (mNotesListAdapter.isAllSelected()) {
item.setChecked(true);
item.setTitle(R.string.menu_deselect_all);
} else {
item.setChecked(false);
item.setTitle(R.string.menu_select_all);
}
}
}
//重新刷新菜单栏时调用
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
//菜单栏点击动作按钮时调用、形参为ActionMode与点击的菜单项
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
return false;
}
//销毁或退出ActionMode时调用。
public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(false);
mNotesListView.setLongClickable(true);
mAddNewNote.setVisibility(View.VISIBLE);
}
//结束ActionMode时调用
public void finishActionMode() {
mActionMode.finish();
}
//检查当前菜单选择情况(针对多选)并进行菜单更新。
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked) {
mNotesListAdapter.setCheckedItem(position, checked);
updateMenu();
}
//菜单项点击时调用。
public boolean onMenuItemClick(MenuItem item) {
if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
Toast.LENGTH_SHORT).show();
return true;
}
int itemId = item.getItemId();
if (itemId == 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()));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();//批处理删除
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
} else if (itemId == R.id.move) {
startQueryDestinationFolders();
} else {
return false;
}
return true;
}
}
//新便签触摸事件监听类。根据触摸事件(比如手在屏幕滑动)修正视图的位置参数
private class NewNoteOnTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
Display display = getWindowManager().getDefaultDisplay();
int screenHeight = display.getHeight();
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());
if (view != null && view.getBottom() > start
&& (view.getTop() < (start + 94))) {
mOriginY = (int) event.getY();
mDispatchY = eventY;
event.setLocation(event.getX(), mDispatchY);
mDispatch = true;
return mNotesListView.dispatchTouchEvent(event);
}
}
} else if (action == MotionEvent.ACTION_MOVE) {
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY;
event.setLocation(event.getX(), mDispatchY);
return mNotesListView.dispatchTouchEvent(event);
}
} else {
if (mDispatch) {
event.setLocation(event.getX(), mDispatchY);
mDispatch = false;
return mNotesListView.dispatchTouchEvent(event);
}
}
return false;
}
};
//已同步便签查询。当进入一个新文件夹时会向数据库发送查询当前文件夹下的便签请求。返回查询结果。这是一个异步查询进程。
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)
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
//背景查询消息处理类。主要功能是将查询后得到的便签在界面显示
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
//下面就是查询结束后对返回数据的处理,完成界面更新。
@Override
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");
}
break;
default:
return;
}
}
}
//通过游标显示文件夹列表菜单,功能为移动便签至某个文件件
private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(R.string.menu_title_select_folder);
final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor);
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
DataUtils.batchMoveToFolder(mContentResolver,
mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which));
Toast.makeText(
NotesListActivity.this,
getString(R.string.format_move_notes_to_folder,
mNotesListAdapter.getSelectedCount(),
adapter.getFolderName(NotesListActivity.this, which)),
Toast.LENGTH_SHORT).show();
mModeCallBack.finishActionMode();
}
});
builder.show();
}
//创建新便签。主要是创建intent并启动。
private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
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
.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");
}
}
return widgets;
}
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> widgets) {
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
mModeCallBack.finishActionMode();
}
}.execute();
}
//文件夹删除功能
private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
return;
}
HashSet<Long> ids = new HashSet<Long>();
ids.add(folderId);
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
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
}
//打开便签功能。形参为NoteItemData其为文件夹或便签的实例。
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
//打开文件夹功能
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 {
mTitleBar.setText(data.getSnippet());
}
mTitleBar.setVisibility(View.VISIBLE);
}
//视图点击事件。实现创建便签。
public void onClick(View v) {
if (v.getId() == R.id.btn_new_note) {
createNewNote();
}
}
//显示软键盘
private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
//不显示软键盘
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
//文件夹新建、查看及重命名功能。
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());
builder.setTitle(getString(R.string.menu_folder_change_name));
} else {
Log.e(TAG, "The long click data item is null");
return;
}
} else {
etName.setText("");
builder.setTitle(this.getString(R.string.menu_create_folder));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideSoftInput(etName);
}
});
final Dialog dialog = builder.setView(view).show();
final Button positive = (Button)dialog.findViewById(android.R.id.button1);
positive.setOnClickListener(new OnClickListener() {
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();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID
+ "=?", new String[] {
String.valueOf(mFocusNoteDataItem.getId())
});
}
} else if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
}
dialog.dismiss();
}
});
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) {
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
} else {
positive.setEnabled(true);
}
}
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}
//用于处理子Acitivity返回父Activity。相当于对按下“返回箭头”的处理。即返回NotesListActivity活动。
@Override
public void onBackPressed() {
switch (mState) {
case SUB_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery();
mTitleBar.setVisibility(View.GONE);
break;
case CALL_RECORD_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
mAddNewNote.setVisibility(View.VISIBLE);
mTitleBar.setVisibility(View.GONE);
startAsyncNotesListQuery();
break;
case NOTE_LIST:
super.onBackPressed();
break;
default:
break;
}
}
//更新小组件。
private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (appWidgetType == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
appWidgetId
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
//查看、删除、修改文件夹名称的浮动菜单栏ContextMenu创建就是长按后出现
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
//创建ContextMenu形参为创建的菜单、长按事件绑定的视图、视图元素信息
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) {
menu.setHeaderTitle(mFocusNoteDataItem.getSnippet());
menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view);
menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete);
menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name);
}
}
};
//下面都是对浮动菜单栏的一些函数
//浮动菜单栏关闭
@Override
public void onContextMenuClosed(Menu menu) {
if (mNotesListView != null) {
mNotesListView.setOnCreateContextMenuListener(null);
}
super.onContextMenuClosed(menu);
}
//浮动菜单栏点击按钮后事件处理。
@Override
public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) {
Log.e(TAG, "The long click data item is null");
return false;
}
switch (item.getItemId()) {
case MENU_FOLDER_VIEW:
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);
builder.setMessage(getString(R.string.alert_message_delete_folder));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteFolder(mFocusNoteDataItem.getId());
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case MENU_FOLDER_CHANGE_NAME:
showCreateOrModifyFolderDialog(false);
break;
default:
break;
}
return true;
}
//下面是OptionsMenu菜单创建的有关函数。
//OptionsMenu创建
@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) {
getMenuInflater().inflate(R.menu.sub_folder, menu);
} else if (mState == ListEditState.CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_record_folder, menu);
} else {
Log.e(TAG, "Wrong state:" + mState);
}
return true;
}
//OptionsMenu点击按钮事件处理
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_new_folder) {
showCreateOrModifyFolderDialog(true);
} else if (itemId == R.id.menu_export_text) {
exportNoteToText();
} else if (itemId == R.id.menu_sync) {
if (isSyncMode()) {
if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {
GTaskSyncService.startSync(this);
} else {
GTaskSyncService.cancelSync(this);
}
} else {
startPreferenceActivity();
}
} else if (itemId == R.id.menu_setting) {
startPreferenceActivity();
} else if (itemId == R.id.menu_new_note) {
createNewNote();
} else if (itemId == R.id.menu_search) {
onSearchRequested();
}
return true;
}
//搜索栏功能实现(就是最上面那个搜索便签得的)
//内部调用了搜索的ui组件
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
return true;
}
//下面使用了异步线程类AsynTask。可在类中可以直接进行UI操作并将后台计算的结果及时的交给UI线程进行UI界面显示。
//将便签内容输出为文本
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... unused) {
return backup.exportToText();
}
@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
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_unmounted));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SUCCESS) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.success_sdcard_export));
builder.setMessage(NotesListActivity.this.getString(
R.string.format_exported_file_location, backup
.getExportedTextFileName(), backup.getExportedTextFileDir()));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SYSTEM_ERROR) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_export));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
}
}.execute();
}
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
//用于跳转到保存的设置界面。这个maybe在isSyncMode==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();
mModeCallBack.onItemCheckedStateChanged(null, position, id,
!mNotesListAdapter.isSelectedItem(position));
}
return;
}
switch (mState) {
case NOTE_LIST:
if (item.getType() == Notes.TYPE_FOLDER
|| item.getType() == Notes.TYPE_SYSTEM) {
openFolder(item);
} else if (item.getType() == Notes.TYPE_NOTE) {
openNode(item);
} else {
Log.e(TAG, "Wrong note type in NOTE_LIST");
}
break;
case SUB_FOLDER:
case CALL_RECORD_FOLDER:
if (item.getType() == Notes.TYPE_NOTE) {
openNode(item);
} else {
Log.e(TAG, "Wrong note type in SUB_FOLDER");
}
break;
default:
break;
}
}
}
}
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 + ")";
mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN,
null,
Notes.CONTENT_NOTE_URI,
FoldersListAdapter.PROJECTION,
selection,
new String[] {
String.valueOf(Notes.TYPE_FOLDER),
String.valueOf(Notes.ID_TRASH_FOLER),
String.valueOf(mCurrentFolderId)
},
NoteColumns.MODIFIED_DATE + " DESC");
}
//处理长按事件。
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);
mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
} else {
Log.e(TAG, "startActionMode fails");
}
} else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener);
}
}
return false;
}
}

@ -0,0 +1,2 @@
# MIUInote

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,980 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
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;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.BackupUtils;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
//NoteListActivity类主要实现小米便签刚进入界面。该类为一种main Activityxml中有定义覆盖点击事件监听及长按事件监听类。。
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 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;
//创建Activity启动其生命周期。完成设置界面样式、初始化资源、且在用户第一次使用App时进行介绍
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_list);
initResources();
/**
* Insert an introduction when user firstly use this application
*/
setAppInfoFromRawRes();
}
//intent返回数据接收函数。形参为请求码、结果码、其他intent返回的数据。主要与NoteEditActivity交互
@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);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
//第一次使用App时调用显示introduction。
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);
if (in != null) {
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
char [] buf = new char[1024];
int len = 0;
while ((len = br.read(buf)) > 0) {
sb.append(buf, 0, len);
}
} else {
Log.e(TAG, "Read introduction file error");
return;
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.RED);
note.setWorkingText(sb.toString());
if (note.saveNote()) {
sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit();
} else {
Log.e(TAG, "Save introduction note error");
return;
}
}
}
//onCreate调用后回调此Activity对用户可见。
@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());
mNotesListView.setOnItemLongClickListener(this);
mNotesListAdapter = new NotesListAdapter(this);
mNotesListView.setAdapter(mNotesListAdapter);
mAddNewNote = (Button) findViewById(R.id.btn_new_note);
mAddNewNote.setOnClickListener(this);
mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener());
mDispatch = false;
mDispatchY = 0;
mOriginY = 0;
mTitleBar = (TextView) findViewById(R.id.tv_title_bar);
mState = ListEditState.NOTE_LIST;
mModeCallBack = new ModeCallback();
}
//ModeCallBack类定义了ActionMode的使用。便签长按事件通过ActionMode执行。
//便签长按后出现选择多个便签删除的菜单栏其在ActionBar中显示。
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu;
private ActionMode mActionMode;
private MenuItem mMoveMenu;
//ActionMode创建函数形参为创建的ActionMode及需要通过该ActionMode显示的Menu对象
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);
} else {
mMoveMenu.setVisible(true);
mMoveMenu.setOnMenuItemClickListener(this);
}
mActionMode = mode;
mNotesListAdapter.setChoiceMode(true);
mNotesListView.setLongClickable(false);
mAddNewNote.setVisibility(View.GONE);
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
R.layout.note_list_dropdown_menu, null);
mode.setCustomView(customView);
mDropDownMenu = new DropdownMenu(NotesListActivity.this,
(Button) customView.findViewById(R.id.selection_menu),
R.menu.note_list_dropdown);
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
public boolean onMenuItemClick(MenuItem item) {
mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());
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);
if (item != null) {
if (mNotesListAdapter.isAllSelected()) {
item.setChecked(true);
item.setTitle(R.string.menu_deselect_all);
} else {
item.setChecked(false);
item.setTitle(R.string.menu_select_all);
}
}
}
//重新刷新菜单栏时调用
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
//菜单栏点击动作按钮时调用、形参为ActionMode与点击的菜单项
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
return false;
}
//销毁或退出ActionMode时调用。
public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(false);
mNotesListView.setLongClickable(true);
mAddNewNote.setVisibility(View.VISIBLE);
}
//结束ActionMode时调用
public void finishActionMode() {
mActionMode.finish();
}
//检查当前菜单选择情况(针对多选)并进行菜单更新。
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked) {
mNotesListAdapter.setCheckedItem(position, checked);
updateMenu();
}
//菜单项点击时调用。
public boolean onMenuItemClick(MenuItem item) {
if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
Toast.LENGTH_SHORT).show();
return true;
}
int itemId = item.getItemId();
if (itemId == 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()));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();//批处理删除
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
} else if (itemId == R.id.move) {
startQueryDestinationFolders();
} else {
return false;
}
return true;
}
}
//新便签触摸事件监听类。根据触摸事件(比如手在屏幕滑动)修正视图的位置参数
private class NewNoteOnTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
Display display = getWindowManager().getDefaultDisplay();
int screenHeight = display.getHeight();
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());
if (view != null && view.getBottom() > start
&& (view.getTop() < (start + 94))) {
mOriginY = (int) event.getY();
mDispatchY = eventY;
event.setLocation(event.getX(), mDispatchY);
mDispatch = true;
return mNotesListView.dispatchTouchEvent(event);
}
}
} else if (action == MotionEvent.ACTION_MOVE) {
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY;
event.setLocation(event.getX(), mDispatchY);
return mNotesListView.dispatchTouchEvent(event);
}
} else {
if (mDispatch) {
event.setLocation(event.getX(), mDispatchY);
mDispatch = false;
return mNotesListView.dispatchTouchEvent(event);
}
}
return false;
}
};
//已同步便签查询。当进入一个新文件夹时会向数据库发送查询当前文件夹下的便签请求。返回查询结果。这是一个异步查询进程。
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)
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
//背景查询消息处理类。主要功能是将查询后得到的便签在界面显示
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
//下面就是查询结束后对返回数据的处理,完成界面更新。
@Override
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");
}
break;
default:
return;
}
}
}
//通过游标显示文件夹列表菜单,功能为移动便签至某个文件件
private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(R.string.menu_title_select_folder);
final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor);
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
DataUtils.batchMoveToFolder(mContentResolver,
mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which));
Toast.makeText(
NotesListActivity.this,
getString(R.string.format_move_notes_to_folder,
mNotesListAdapter.getSelectedCount(),
adapter.getFolderName(NotesListActivity.this, which)),
Toast.LENGTH_SHORT).show();
mModeCallBack.finishActionMode();
}
});
builder.show();
}
//创建新便签。主要是创建intent并启动。
private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
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
.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");
}
}
return widgets;
}
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> widgets) {
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
mModeCallBack.finishActionMode();
}
}.execute();
}
//文件夹删除功能
private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
return;
}
HashSet<Long> ids = new HashSet<Long>();
ids.add(folderId);
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
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
}
//打开便签功能。形参为NoteItemData其为文件夹或便签的实例。
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
//打开文件夹功能
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 {
mTitleBar.setText(data.getSnippet());
}
mTitleBar.setVisibility(View.VISIBLE);
}
//视图点击事件。实现创建便签。
public void onClick(View v) {
if (v.getId() == R.id.btn_new_note) {
createNewNote();
}
}
//显示软键盘
private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
//不显示软键盘
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
//文件夹新建、查看及重命名功能。
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());
builder.setTitle(getString(R.string.menu_folder_change_name));
} else {
Log.e(TAG, "The long click data item is null");
return;
}
} else {
etName.setText("");
builder.setTitle(this.getString(R.string.menu_create_folder));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideSoftInput(etName);
}
});
final Dialog dialog = builder.setView(view).show();
final Button positive = (Button)dialog.findViewById(android.R.id.button1);
positive.setOnClickListener(new OnClickListener() {
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();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID
+ "=?", new String[] {
String.valueOf(mFocusNoteDataItem.getId())
});
}
} else if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
}
dialog.dismiss();
}
});
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) {
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
} else {
positive.setEnabled(true);
}
}
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}
//用于处理子Acitivity返回父Activity。相当于对按下“返回箭头”的处理。即返回NotesListActivity活动。
@Override
public void onBackPressed() {
switch (mState) {
case SUB_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery();
mTitleBar.setVisibility(View.GONE);
break;
case CALL_RECORD_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
mAddNewNote.setVisibility(View.VISIBLE);
mTitleBar.setVisibility(View.GONE);
startAsyncNotesListQuery();
break;
case NOTE_LIST:
super.onBackPressed();
break;
default:
break;
}
}
//更新小组件。
private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (appWidgetType == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
appWidgetId
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
//查看、删除、修改文件夹名称的浮动菜单栏ContextMenu创建就是长按后出现
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
//创建ContextMenu形参为创建的菜单、长按事件绑定的视图、视图元素信息
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) {
menu.setHeaderTitle(mFocusNoteDataItem.getSnippet());
menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view);
menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete);
menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name);
}
}
};
//下面都是对浮动菜单栏的一些函数
//浮动菜单栏关闭
@Override
public void onContextMenuClosed(Menu menu) {
if (mNotesListView != null) {
mNotesListView.setOnCreateContextMenuListener(null);
}
super.onContextMenuClosed(menu);
}
//浮动菜单栏点击按钮后事件处理。
@Override
public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) {
Log.e(TAG, "The long click data item is null");
return false;
}
switch (item.getItemId()) {
case MENU_FOLDER_VIEW:
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);
builder.setMessage(getString(R.string.alert_message_delete_folder));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteFolder(mFocusNoteDataItem.getId());
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case MENU_FOLDER_CHANGE_NAME:
showCreateOrModifyFolderDialog(false);
break;
default:
break;
}
return true;
}
//下面是OptionsMenu菜单创建的有关函数。
//OptionsMenu创建
@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) {
getMenuInflater().inflate(R.menu.sub_folder, menu);
} else if (mState == ListEditState.CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_record_folder, menu);
} else {
Log.e(TAG, "Wrong state:" + mState);
}
return true;
}
//OptionsMenu点击按钮事件处理
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_new_folder) {
showCreateOrModifyFolderDialog(true);
} else if (itemId == R.id.menu_export_text) {
exportNoteToText();
} else if (itemId == R.id.menu_sync) {
if (isSyncMode()) {
if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {
GTaskSyncService.startSync(this);
} else {
GTaskSyncService.cancelSync(this);
}
} else {
startPreferenceActivity();
}
} else if (itemId == R.id.menu_setting) {
startPreferenceActivity();
} else if (itemId == R.id.menu_new_note) {
createNewNote();
} else if (itemId == R.id.menu_search) {
onSearchRequested();
}
return true;
}
//搜索栏功能实现(就是最上面那个搜索便签得的)
//内部调用了搜索的ui组件
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
return true;
}
//下面使用了异步线程类AsynTask。可在类中可以直接进行UI操作并将后台计算的结果及时的交给UI线程进行UI界面显示。
//将便签内容输出为文本
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... unused) {
return backup.exportToText();
}
@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
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_unmounted));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SUCCESS) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.success_sdcard_export));
builder.setMessage(NotesListActivity.this.getString(
R.string.format_exported_file_location, backup
.getExportedTextFileName(), backup.getExportedTextFileDir()));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SYSTEM_ERROR) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_export));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
}
}.execute();
}
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
//用于跳转到保存的设置界面。这个maybe在isSyncMode==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();
mModeCallBack.onItemCheckedStateChanged(null, position, id,
!mNotesListAdapter.isSelectedItem(position));
}
return;
}
switch (mState) {
case NOTE_LIST:
if (item.getType() == Notes.TYPE_FOLDER
|| item.getType() == Notes.TYPE_SYSTEM) {
openFolder(item);
} else if (item.getType() == Notes.TYPE_NOTE) {
openNode(item);
} else {
Log.e(TAG, "Wrong note type in NOTE_LIST");
}
break;
case SUB_FOLDER:
case CALL_RECORD_FOLDER:
if (item.getType() == Notes.TYPE_NOTE) {
openNode(item);
} else {
Log.e(TAG, "Wrong note type in SUB_FOLDER");
}
break;
default:
break;
}
}
}
}
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 + ")";
mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN,
null,
Notes.CONTENT_NOTE_URI,
FoldersListAdapter.PROJECTION,
selection,
new String[] {
String.valueOf(Notes.TYPE_FOLDER),
String.valueOf(Notes.ID_TRASH_FOLER),
String.valueOf(mCurrentFolderId)
},
NoteColumns.MODIFIED_DATE + " DESC");
}
//处理长按事件。
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);
mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
} else {
Log.e(TAG, "startActionMode fails");
}
} else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener);
}
}
return false;
}
}

@ -20,7 +20,7 @@
android:versionCode="1"
android:versionName="0.1" >
<uses-sdk android:minSdkVersion="14" />
<uses-sdk android:minSdkVersion="17" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -26,7 +26,7 @@ import android.util.Log;
import java.util.HashMap;
public class Contact {
private static HashMap<String, String> sContactCache;
private static HashMap<String, String> sContactCache;//联系人缓存
private static final String TAG = "Contact";
private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
@ -34,7 +34,7 @@ public class Contact {
+ " AND " + Data.RAW_CONTACT_ID + " IN "
+ "(SELECT raw_contact_id "
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
+ " WHERE min_match = '+')";//规定SQL查询语句
public static String getContact(Context context, String phoneNumber) {
if(sContactCache == null) {
@ -52,8 +52,8 @@ public class Contact {
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
null);//进行缓存查询。如果缓存没有填充SQL查询语言
//进行查询并返回结果
if (cursor != null && cursor.moveToFirst()) {
try {
String name = cursor.getString(0);

@ -41,7 +41,6 @@ public class Notes {
public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type";
public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id";
public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";
public static final String INTENT_EXTRA_TEMPLE_TEXT="net.micode.notes.temple_text";
public static final int TYPE_WIDGET_INVALIDE = -1;
public static final int TYPE_WIDGET_2X = 0;
@ -166,8 +165,6 @@ public class Notes {
* <P> Type : INTEGER (long) </P>
*/
public static final String VERSION = "version";
public static final String PASSWORD= "password";
}
public interface DataColumns {

@ -30,7 +30,7 @@ import net.micode.notes.data.Notes.NoteColumns;
public class NotesDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "note.db";
private static final int DB_VERSION = 5;
private static final int DB_VERSION = 4;
public interface TABLE {
public static final String NOTE = "note";
@ -60,8 +60,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.PASSWORD + " TEXT DEFAULT NULL " +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")";
private static final String CREATE_DATA_TABLE_SQL =
@ -322,10 +321,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
upgradeToV4(db);
oldVersion++;
}
if(oldVersion == 4){
upgradeToV5(db);
oldVersion++;
}
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
@ -364,9 +359,4 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
}
private void upgradeToV5(SQLiteDatabase db){
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.PASSWORD
+ " TEXT DEFAULT NULL ");
}
}

@ -32,7 +32,7 @@ public class MetaData extends Task {
public void setMeta(String gid, JSONObject metaInfo) {
try {
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);//将gid放进去
} catch (JSONException e) {
Log.e(TAG, "failed to put related gid");
}
@ -55,7 +55,7 @@ public class MetaData extends Task {
if (getNotes() != null) {
try {
JSONObject metaInfo = new JSONObject(getNotes().trim());
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);//从metaInfo中获取该字段的值。metaInfo应该是一个类似于数据集合的东西
} catch (JSONException e) {
Log.w(TAG, "failed to get related gid");
mRelatedGid = null;

@ -19,8 +19,9 @@ package net.micode.notes.gtask.data;
import android.database.Cursor;
import org.json.JSONObject;
//本地及云端数据更新、同步
public abstract class Node {
//下述常量用于表示对本地及云端数据的操作号
public static final int SYNC_ACTION_NONE = 0;
public static final int SYNC_ACTION_ADD_REMOTE = 1;
@ -43,7 +44,7 @@ public abstract class Node {
private String mName;
private long mLastModified;
private long mLastModified;//最后一次修改时间
private boolean mDeleted;
@ -54,7 +55,7 @@ public abstract class Node {
mDeleted = false;
}
public abstract JSONObject getCreateAction(int actionId);
public abstract JSONObject getCreateAction(int actionId);//actionId 可能是用于标识不同类型的创建操作的参数
public abstract JSONObject getUpdateAction(int actionId);
@ -64,7 +65,7 @@ public abstract class Node {
public abstract JSONObject getLocalJSONFromContent();
public abstract int getSyncAction(Cursor c);
public abstract int getSyncAction(Cursor c);//获取同步操作
public void setGid(String gid) {
this.mGid = gid;

@ -34,11 +34,11 @@ import net.micode.notes.gtask.exception.ActionFailureException;
import org.json.JSONException;
import org.json.JSONObject;
//小米便签底层数据库相关操作
public class SqlData {
private static final String TAG = SqlData.class.getSimpleName();
private static final int INVALID_ID = -99999;
private static final int INVALID_ID = -99999;//无效ID
public static final String[] PROJECTION_DATA = new String[] {
DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1,
@ -57,7 +57,7 @@ public class SqlData {
private ContentResolver mContentResolver;
private boolean mIsCreate;
private boolean mIsCreate;//用于表示sqldata初始化的两种方式
private long mDataId;
@ -71,6 +71,7 @@ public class SqlData {
private ContentValues mDiffDataValues;
//第一种初始化形式
public SqlData(Context context) {
mContentResolver = context.getContentResolver();
mIsCreate = true;
@ -96,7 +97,7 @@ public class SqlData {
mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
}
//设置共享数据
public void setContent(JSONObject js) throws JSONException {
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
if (mIsCreate || mDataId != dataId) {
@ -125,7 +126,7 @@ public class SqlData {
String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";
if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {
mDiffDataValues.put(DataColumns.DATA3, dataContentData3);
mDiffDataValues.put(DataColumns.DATA3, dataContentData3);//键值对
}
mDataContentData3 = dataContentData3;
}
@ -143,29 +144,34 @@ public class SqlData {
js.put(DataColumns.DATA3, mDataContentData3);
return js;
}
//将当前所做修改保存至数据库
//下面创建及更新两种不同操作有由IsCreate控制也对应了sqlData初始化的两种方式创建新的从cursor更新
//创建新数据笔记并将其同步至数据库
//感觉在这里mDiffdataValues代表需要新写的键值对(记录已发生更改的属性?),通常与数据库交互,保存数据变化,就是用户新编辑的。
// ContentResolver 是用于访问应用程序的数据存储区域的关键类之一。与应用程序的数据提供者(如数据库、内容提供器等)进行交互
public void commit(long noteId, boolean validateVersion, long version) {
if (mIsCreate) {
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
mDiffDataValues.remove(DataColumns.ID);
}
}//如果ID无效以及mDiffdataValues包含这个ID键值对移除这个键值对
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);//把mDiffdataValues插到这个URI的位置
try {
mDataId = Long.valueOf(uri.getPathSegments().get(1));
mDataId = Long.valueOf(uri.getPathSegments().get(1));//再把这个uri对应的数据的ID取出来。如果提取失败说明创建失败
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
} else {
if (mDiffDataValues.size() > 0) {
//笔记更新
if (mDiffDataValues.size() > 0) { //说明有需要更新的
int result = 0;
if (!validateVersion) {
if (!validateVersion) { //如果版本不需要更新则直接对指定URI进行更新
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);
} else {
} else { //版本与指定内容一起更新
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues,
" ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE

@ -37,12 +37,13 @@ import org.json.JSONObject;
import java.util.ArrayList;
//底层数据库操作是SqlData的父集。相对而言是真正意义上的数据
public class SqlNote {
private static final String TAG = SqlNote.class.getSimpleName();
private static final int INVALID_ID = -99999;
//PROJECTION_NOTE 里面包括了许多列的名字,在后面数据库查询中用到
public static final String[] PROJECTION_NOTE = new String[] {
NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,
@ -51,7 +52,7 @@ public class SqlNote {
NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID,
NoteColumns.VERSION
};
//下面是17个列的编号
public static final int ID_COLUMN = 0;
public static final int ALERTED_DATE_COLUMN = 1;
@ -120,18 +121,19 @@ public class SqlNote {
private ContentValues mDiffNoteValues;
private ArrayList<SqlData> mDataList;
private ArrayList<SqlData> mDataList;//ArrayList是动态数组用于储存类型为SqlData的数据。
//SqlNote一种构造方式。这里为新建一个
public SqlNote(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = true;
mId = INVALID_ID;
mAlertDate = 0;
mBgColorId = ResourceParser.getDefaultBgId(context);
mCreatedDate = System.currentTimeMillis();
mBgColorId = ResourceParser.getDefaultBgId(context);//获取颜色、图片等元素ID用以渲染界面
mCreatedDate = System.currentTimeMillis();//当前系统时间
mHasAttachment = 0;
mModifiedDate = System.currentTimeMillis();
mModifiedDate = System.currentTimeMillis();//最后一次数据更新时间
mParentId = 0;
mSnippet = "";
mType = Notes.TYPE_NOTE;
@ -142,7 +144,7 @@ public class SqlNote {
mDiffNoteValues = new ContentValues();
mDataList = new ArrayList<SqlData>();
}
//初始化方式。通过cursor c初始化
public SqlNote(Context context, Cursor c) {
mContext = context;
mContentResolver = context.getContentResolver();
@ -150,31 +152,34 @@ public class SqlNote {
loadFromCursor(c);
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
loadDataContent();//数据加载等操作
mDiffNoteValues = new ContentValues();
}
//第三种初始化方式只不过这次也不是新建的。通过id初始化
public SqlNote(Context context, long id) {
mContext = context;
mContentResolver = context.getContentResolver();
//ContentResolver 是用于访问应用程序的数据存储区域的关键类之一。与应用程序的数据提供者(如数据库、内容提供器等)进行交互
mIsCreate = false;
loadFromCursor(id);
loadFromCursor(id);//将cursor数据下载到SqlNote
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
mDiffNoteValues = new ContentValues();//创建一个空的 ContentValues 对象,用于存储键值对数据,通常用于与数据库进行交互或保存数据的变化。
}
//从数据库中查询笔记数据,将结果加载到对象中(cursor c)
private void loadFromCursor(long id) {
Cursor c = null;
try {
//query查询函数。
//下面函数先匹配给的id与需要查询的id是否相同然后查询PROJECTION_NOTE里列的数据将其赋给c
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",
new String[] {
String.valueOf(id)
}, null);
if (c != null) {
c.moveToNext();
c.moveToNext();//移动游标读取数据
loadFromCursor(c);
} else {
Log.w(TAG, "loadFromCursor: cursor = null");
@ -199,7 +204,7 @@ public class SqlNote {
mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);
mVersion = c.getLong(VERSION_COLUMN);
}
//查询笔记数据内容
private void loadDataContent() {
Cursor c = null;
mDataList.clear();
@ -228,6 +233,7 @@ public class SqlNote {
public boolean setContent(JSONObject js) {
try {
//根据不同的便签类型操作。
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
Log.w(TAG, "cannot set system folder");
@ -253,7 +259,9 @@ public class SqlNote {
mDiffNoteValues.put(NoteColumns.ID, id);
}
mId = id;
//下面的操作是根据获得的新数据更新一些变量值。
// 其中m开的头的变量eg mBgColorId 表示当前的属性值)下面的代码就是根据新数据来更新这些值
//更新笔记提醒时间属性
long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note
.getLong(NoteColumns.ALERTED_DATE) : 0;
if (mIsCreate || mAlertDate != alertDate) {
@ -262,7 +270,7 @@ public class SqlNote {
mAlertDate = alertDate;
int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note
.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);//后面这个指获取默认的背景信息
if (mIsCreate || mBgColorId != bgColorId) {
mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
}
@ -330,12 +338,13 @@ public class SqlNote {
mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
}
mOriginParent = originParent;
//从mDataList中寻找与dataArray中ID相同的元素将其放入sqldata中
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null;
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
//这个for循环为增强型即每次遍历mDataList中的一个数组并将这个数组赋给temp
for (SqlData temp : mDataList) {
if (dataId == temp.getId()) {
sqlData = temp;
@ -358,7 +367,7 @@ public class SqlNote {
}
return true;
}
//将笔记的属性及数据以json形式返回以便后续使用
public JSONObject getContent() {
try {
JSONObject js = new JSONObject();
@ -367,7 +376,7 @@ public class SqlNote {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
//下面是获得单个(或多个)便签对象的数据
JSONObject note = new JSONObject();
if (mType == Notes.TYPE_NOTE) {
note.put(NoteColumns.ID, mId);
@ -392,7 +401,7 @@ public class SqlNote {
}
}
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { //这是获得文件夹的数据
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.SNIPPET, mSnippet);
@ -440,7 +449,9 @@ public class SqlNote {
return mType == Notes.TYPE_NOTE;
}
//数据提交或更新(将当前更改保存到数据库)
public void commit(boolean validateVersion) {
//创建新笔记
if (mIsCreate) {
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
mDiffNoteValues.remove(NoteColumns.ID);
@ -462,7 +473,7 @@ public class SqlNote {
sqlData.commit(mId, false, -1);
}
}
} else {
} else { //更新现有笔记
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
Log.e(TAG, "No such note");
throw new IllegalStateException("Try to update note with invalid id");
@ -486,7 +497,7 @@ public class SqlNote {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
//提交关联数据
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, validateVersion, mVersion);
@ -495,9 +506,10 @@ public class SqlNote {
}
// refresh local info
loadFromCursor(mId);
//刷新本地信息
loadFromCursor(mId);//加载数据
if (mType == Notes.TYPE_NOTE)
loadDataContent();
loadDataContent();//加载数据内容
mDiffNoteValues.clear();
mIsCreate = false;

@ -49,8 +49,8 @@ public class Task extends Node {
super();
mCompleted = false;
mNotes = null;
mPriorSibling = null;
mParent = null;
mPriorSibling = null;//TaskList中当前task的前一个task
mParent = null;//当前task所在点List
mMetaInfo = null;
}
@ -66,8 +66,8 @@ public class Task extends Node {
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));//当前task在其父级task的索引
//构建一个包含任务的名称、创建者ID和实体类型信息的 JSON 对象entity
// entity_delta
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
@ -123,7 +123,7 @@ public class Task extends Node {
if (getNotes() != null) {
entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
}
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());//实体的删除状态
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
} catch (JSONException e) {
@ -135,6 +135,7 @@ public class Task extends Node {
return js;
}
//remote指云端数据库反正不是本地
public void setContentByRemoteJSON(JSONObject js) {
if (js != null) {
try {
@ -192,6 +193,7 @@ public class Task extends Node {
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
//如果这个data的mime_type与NOTE常量相同则将this所指代的对象的名字设为内容CONTENT)
if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
setName(data.getString(DataColumns.CONTENT));
break;
@ -225,7 +227,7 @@ public class Task extends Node {
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
return js;
} else {
// synced task
// synced task 同步任务
JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
@ -247,17 +249,18 @@ public class Task extends Node {
}
}
//将metaData对象中的笔记信息假设是一个JSON字符串解析为一个JSON对象并将其赋值给mMetaInfo
public void setMetaInfo(MetaData metaData) {
if (metaData != null && metaData.getNotes() != null) {
try {
mMetaInfo = new JSONObject(metaData.getNotes());
mMetaInfo = new JSONObject(metaData.getNotes());//用getNodes中的信息初始化mMetaInfo
} catch (JSONException e) {
Log.w(TAG, e.toString());
mMetaInfo = null;
}
}
}
//数据库与本地数据同步
public int getSyncAction(Cursor c) {
try {
JSONObject noteInfo = null;
@ -312,7 +315,7 @@ public class Task extends Node {
}
public boolean isWorthSaving() {
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)//trim作用为去掉空格
|| (getNotes() != null && getNotes().trim().length() > 0);
}

@ -216,6 +216,8 @@ public class TaskList extends Node {
return SYNC_ACTION_ERROR;
}
//任务队列大小
public int getChildTaskCount() {
return mChildren.size();
}
@ -226,6 +228,7 @@ public class TaskList extends Node {
ret = mChildren.add(task);
if (ret) {
// need to set prior sibling and parent
//如果子任务链表为空则前一个兄弟任务为null如果不为空则将前一个子任务设置为当前任务末端。
task.setPriorSibling(mChildren.isEmpty() ? null : mChildren
.get(mChildren.size() - 1));
task.setParent(this);
@ -234,6 +237,7 @@ public class TaskList extends Node {
return ret;
}
//在某个位置插入一个子任务
public boolean addChildTask(Task task, int index) {
if (index < 0 || index > mChildren.size()) {
Log.e(TAG, "add child task: invalid index");
@ -281,6 +285,8 @@ public class TaskList extends Node {
return ret;
}
//移动某子任务到index位置
public boolean moveChildTask(Task task, int index) {
if (index < 0 || index >= mChildren.size()) {
@ -321,6 +327,7 @@ public class TaskList extends Node {
return mChildren.get(index);
}
//和308行函数的区别在于308行新建一个Task返回下面的直接把原本的Task返回
public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) {
if (task.getGid().equals(gid))

@ -57,6 +57,7 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
mTaskManager.cancelSync();
}
//通知异步任务更新进度信息。
public void publishProgess(String message) {
publishProgress(new String[] {
message
@ -64,10 +65,15 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}
private void showNotification(int tickerId, String content) {
//创建一个Notification对象并设置其图标、文本和时间戳等属性。其中R.drawable.notification表示通知的图标
// mContext.getString(tickerId)表示通知的文本System.currentTimeMillis()表示通知的时间戳。
Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis());
//将通知设置为默认显示灯光效果。
notification.defaults = Notification.DEFAULT_LIGHTS;
//将通知设置为默认显示灯光效果。
notification.flags = Notification.FLAG_AUTO_CANCEL;
PendingIntent pendingIntent;
if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
@ -77,17 +83,21 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0);
}
// notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
// pendingIntent);
//设置通知的最新事件信息包括应用程序名称、通知内容和要启动的pendingIntent。
//notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
// pendingIntent);
//显示通知并指定通知的标识符为GTASK_SYNC_NOTIFICATION_ID。
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
}
//更新进度信息,并执行后台的同步任务
@Override
protected Integer doInBackground(Void... unused) {
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));
return mTaskManager.sync(mContext, this);
}
//后台任务执行过程中更新进度信息,并通过通知和广播将进度信息传递给其他组件
@Override
protected void onProgressUpdate(String... progress) {
@ -96,7 +106,7 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
((GTaskSyncService) mContext).sendBroadcast(progress[0]);
}
}
//异步任务执行完成后,根据任务执行的结果执行不同的操作,并通过通知和回调将结果传递给其他组件.
@Override
protected void onPostExecute(Integer result) {
if (result == GTaskManager.STATE_SUCCESS) {
@ -111,13 +121,15 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
showNotification(R.string.ticker_cancel, mContext
.getString(R.string.error_sync_cancelled));
}
//新开线程回调监听器
if (mOnCompleteListener != null) {
new Thread(new Runnable() {
public void run() {
//线程创建后执行以下操作
mOnCompleteListener.onComplete();
}
}).start();
}).start();//启动线程
}
}
}

@ -110,9 +110,9 @@ public class GTaskClient {
}
public boolean login(Activity activity) {
// we suppose that the cookie would expire after 5 minutes
// we suppose that the cookie would expire(失效) after 5 minutes
// then we need to re-login
final long interval = 1000 * 60 * 5;
final long interval = 1000 * 60 * 5;//5min的ms
if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false;
}
@ -130,13 +130,14 @@ public class GTaskClient {
}
mLastLoginTime = System.currentTimeMillis();
String authToken = loginGoogleAccount(activity, false);
String authToken = loginGoogleAccount(activity, false);//谷歌账号登陆
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
// login with custom domain if necessary
// login with custom domain if necessary 反正也是管登陆的.
//toLowerCase 字符串转成小写
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase()
.endsWith("googlemail.com"))) {
StringBuilder url = new StringBuilder(GTASK_URL).append("a/");
@ -176,6 +177,7 @@ public class GTaskClient {
String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null;
//遍历所有账户列表,看有没有和所给的一样的
for (Account a : accounts) {
if (a.name.equals(accountName)) {
account = a;
@ -189,7 +191,12 @@ public class GTaskClient {
return null;
}
// get the token now
//下面是获得目标账户认证令牌。
//调用accountManager.getAuthToken函数异步获取认证令牌返回的是一个AccountManagerFuture<Bundle>对象。
// 然后调用getResult方法尝试获取包含认证令牌的Bundle对象。如果成功获得该Bundle就从中提取出认证令牌并赋值给authToken。
// 如果invalidateToken标志为true那么就使当前的认证令牌失效并且再次调用loginGoogleAccount方法获取新的认证令牌。
// 如果在这个过程中捕获到任何异常就打印错误日志并将authToken设置为null。
// get the token now token有认证的意思
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null);
try {
@ -207,6 +214,7 @@ public class GTaskClient {
return authToken;
}
//如果刚开始没登录成功GTask那么我再取一次授权令牌再试一次如果还没成功则登陆gtask失败
private boolean tryToLoginGtask(Activity activity, String authToken) {
if (!loginGtask(authToken)) {
// maybe the auth token is out of date, now let's invalidate the
@ -225,10 +233,13 @@ public class GTaskClient {
return true;
}
//使用给出的authToken进行登录操作并获取客户端版本信息
private boolean loginGtask(String authToken) {
int timeoutConnection = 10000;
int timeoutSocket = 15000;
HttpParams httpParameters = new BasicHttpParams();
//下面是鼓捣http连接的。HttpClient是Apache HttpClient库提供的一个类用于设立HTTP连接发送HTTP请求并处理HTTP响应。
HttpParams httpParameters = new BasicHttpParams();//param参数
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
mHttpClient = new DefaultHttpClient(httpParameters);
@ -239,7 +250,7 @@ public class GTaskClient {
// login gtask
try {
String loginUrl = mGetUrl + "?auth=" + authToken;
HttpGet httpGet = new HttpGet(loginUrl);
HttpGet httpGet = new HttpGet(loginUrl);//用于发送 HTTP GET 请求到服务器并获取响应
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
@ -255,7 +266,7 @@ public class GTaskClient {
Log.w(TAG, "it seems that there is no auth cookie");
}
// get the client version
// get the client version 这在切割js代码把字符串提出来
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
@ -284,6 +295,7 @@ public class GTaskClient {
return mActionId++;
}
//请求头信息
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
@ -291,21 +303,24 @@ public class GTaskClient {
return httpPost;
}
//获取HTTP响应的内容内容编码方式—
private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null;
if (entity.getContentEncoding() != null) {
contentEncoding = entity.getContentEncoding().getValue();
Log.d(TAG, "encoding: " + contentEncoding);
}
//根据响应的内容编码方式对输入流进行处理
InputStream input = entity.getContent();
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {//是否经过gzip文件压缩
input = new GZIPInputStream(entity.getContent());
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {
Inflater inflater = new Inflater(true);
input = new InflaterInputStream(entity.getContent(), inflater);
}
//将输入流转换为字符串
try {
InputStreamReader isr = new InputStreamReader(input);
BufferedReader br = new BufferedReader(isr);
@ -323,6 +338,7 @@ public class GTaskClient {
}
}
//服务器发送JSON格式的数据并返回服务器响应的JSON格式数据
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
@ -331,6 +347,7 @@ public class GTaskClient {
HttpPost httpPost = createHttpPost();
try {
//将json转为字符串往列表里放
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
list.add(new BasicNameValuePair("r", js.toString()));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
@ -360,6 +377,8 @@ public class GTaskClient {
}
}
//创建新任务的方法它构建了一个包含任务操作和客户端版本号的JSON对象并将其作为POST请求发送给服务器。
// 然后从服务器响应中解析出新创建任务的ID并将其设置给task对象
public void createTask(Task task) throws NetworkFailureException {
commitUpdate();
try {
@ -412,6 +431,8 @@ public class GTaskClient {
}
}
//提交任务更新的方法
//mUpdateArray是否为null。如果是表示没有需要提交的更新直接退出方法
public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) {
try {
@ -447,6 +468,7 @@ public class GTaskClient {
}
}
//移动任务
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
commitUpdate();

@ -168,6 +168,8 @@ public class GTaskManager {
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
}
//init初始化。先初始化metalist再 搞tasklist在搞具体的task
private void initGTaskList() throws NetworkFailureException {
if (mCancelled)
return;
@ -247,6 +249,8 @@ public class GTaskManager {
}
}
//从本地的备忘录应用中查询未被删除的备忘录条目并与Google Tasks服务同步。
private void syncContent() throws NetworkFailureException {
int syncType;
Cursor c = null;
@ -288,6 +292,7 @@ public class GTaskManager {
// sync folder first
syncFolder();
//从本地的备忘录应用中查询已存在的备忘录条目并与Google Tasks服务同步
// for note existing in database
try {
@ -326,7 +331,7 @@ public class GTaskManager {
}
}
// go through remaining items
// go through remaining items 同步在Google Tasks服务中存在、但在本地备忘录应用中不存在的备忘录条目
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Node> entry = iter.next();
@ -336,7 +341,9 @@ public class GTaskManager {
// mCancelled can be set by another thread, so we neet to check one by
// one
// clear local delete table
// clear local delete table在同步完成后对于本地备忘录应用中已删除的备忘录条目进行批量删除操作
// 并刷新本地备忘录应用与Google Tasks服务之间的同步ID。
// batch 批量
if (!mCancelled) {
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
throw new ActionFailureException("failed to batch-delete local deleted notes");
@ -362,6 +369,7 @@ public class GTaskManager {
}
// for root folder
//Google Tasks服务中的备忘录条目是否需要更新名称并决定调用何种类型的同步操作
try {
c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null);
@ -374,6 +382,7 @@ public class GTaskManager {
mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);
mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);
// for system folder, only update remote name if necessary
//下面是Googletask服务备忘录与本地 的是否有?或不存在。有就是更新,没有就是增加
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
@ -391,6 +400,8 @@ public class GTaskManager {
}
// for call-note folder
//查询特定系统文件夹ID为Notes.ID_CALL_RECORD_FOLDER表示通话记录文件夹对应的备忘录条目
// 并根据查询结果执行相应的同步操作
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)",
new String[] {
@ -424,7 +435,8 @@ public class GTaskManager {
}
}
// for local existing folders
// for local existing folders 查询本地备忘录应用中所有非"垃圾箱"文件夹类型的备忘录条目,
// 并根据查询结果执行相应的同步操作
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[] {
@ -440,6 +452,8 @@ public class GTaskManager {
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
syncType = node.getSyncAction(c);
} else {
//当node为null且GTASK_ID列为空时说明该备忘录条目是本地新增的需要将其添加到远程服务器进行同步。
//当node为null且GTASK_ID列不为空时说明该备忘录条目是远程删除的需要将其从本地进行同步删除。
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// local add
syncType = Node.SYNC_ACTION_ADD_REMOTE;
@ -645,6 +659,7 @@ public class GTaskManager {
// add meta
updateRemoteMeta(task.getGid(), sqlNote);
} else {
//查找是否已经存在一个与给定的文件夹名称匹配的远程任务列表
TaskList tasklist = null;
// we need to skip folder if it has already existed
@ -670,6 +685,7 @@ public class GTaskManager {
break;
}
}
//所以,上面的操作是找到了匹配的远程任务列表,下面是没找到,我们创个新的
// no match we can add now
if (tasklist == null) {
@ -746,6 +762,8 @@ public class GTaskManager {
}
}
//确保本地笔记和GTask上的笔记同步并更新本地笔记的同步ID
private void refreshLocalSyncId() throws NetworkFailureException {
if (mCancelled) {
return;

@ -69,8 +69,10 @@ public class GTaskSyncService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras();
Bundle bundle = intent.getExtras();//获取附加数据
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
//从Bundle中获取与指定键对应的整数值(ACTION_STRING_NAME)。如果指定键存在且对应的值为整数类型,则返回该整数值;
// 否则返回默认的整数值ACTION_INVALID。
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
case ACTION_START_SYNC:
startSync();
@ -87,6 +89,7 @@ public class GTaskSyncService extends Service {
}
@Override
//内存不足时取消同步任务
public void onLowMemory() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
@ -105,13 +108,17 @@ public class GTaskSyncService extends Service {
sendBroadcast(intent);
}
//在指定的Activity中开启同步任务通过调用GTaskSyncService服务来执行同步操作。
public static void startSync(Activity activity) {
GTaskManager.getInstance().setActivityContext(activity);
Intent intent = new Intent(activity, GTaskSyncService.class);
//增加键值对
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
activity.startService(intent);
}
//取消同步操作
public static void cancelSync(Context context) {
Intent intent = new Intent(context, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);

@ -41,6 +41,8 @@ public class Note {
/**
* Create a new note id for adding a new note to databases
*/
//在数据库中创建一条新的笔记记录并返回新创建的笔记的ID
public static synchronized long getNewNoteId(Context context, long folderId) {
// Create a new note in the database
ContentValues values = new ContentValues();
@ -49,11 +51,14 @@ public class Note {
values.put(NoteColumns.MODIFIED_DATE, createdTime);
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
//这个新笔记的父节点就是文件夹ID。
values.put(NoteColumns.PARENT_ID, folderId);
//将values对象插入到数据库中并返回插入的记录的URI
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
try {
//解析返回的uri得到新笔记ID值
noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
@ -114,6 +119,7 @@ public class Note {
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
* note data info
*/
//估计是看update的过程有没有出现错误。如果有就报错了
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
@ -178,6 +184,7 @@ public class Note {
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
//该方法用于将笔记数据推送到内容解析器Content Resolver中。
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* Check for safety
@ -186,11 +193,12 @@ public class Note {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();//操作列表
ContentProviderOperation.Builder builder = null;
//是否有文本数据需要更新
if(mTextDataValues.size() > 0) {
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
//mTextDataId=0表示需要新插入文本数据
if (mTextDataId == 0) {
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
@ -205,6 +213,7 @@ public class Note {
} else {
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
//设置需要更新的字段及值
builder.withValues(mTextDataValues);
operationList.add(builder.build());
}
@ -235,6 +244,7 @@ public class Note {
if (operationList.size() > 0) {
try {
//将操作列表operationList应用到内容解析器
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
return (results == null || results.length == 0 || results[0] == null) ? null

@ -1,435 +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.
* 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.model;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
/**
*
* @ProjectName: xiaomibianqian
* @Package: model
* @ClassName: WorkingNote
* @Description: 便
* 便便
* NoteEditActivitiesNotesfrom dataNote NoteListActivity SqlData
* NoteSettingChangedListenerNoteEditActivities
*
* @Author: zhoushiyu_br
* @CreateDate: 2023.12.16
* @UpdateUser:
* @UpdateDate: 2023.12.21
* @UpdateRemark:
* @Version: PickupRAIN
*/
public class WorkingFolder {
// Note for the working note
private String mPassword;
private Note mNote;//这个在Note里面定义了Note类的基本类型
// Note Id
private long mNoteId;
// Note content
private String mContent;
// Note mode
private int mMode;
private long mAlertDate;
private long mModifiedDate;//最后的修改日期
private int mBgColorId;//定义的颜色的ID
private int mWidgetId;//定义了一个int类型用来定位到哪一个小组件被使用
private int mWidgetType;//定义了一个int类型用来区分使用了什么类型的小组件
private long mFolderId;//用来定位便签放在哪个文件夹
private Context mContext;
public boolean PRIVATE_MODE = false;
private static final String TAG = "WorkingNote"; //定义为静态变量,保证不能再被更改。防止数据出现异常
private boolean mIsDeleted; //定义一个布尔变量,用来保存是否要被删除。通过查看用法,发现在后面保存|修改的时候要进行判断。
private NoteSettingChangedListener mNoteSettingStatusListener;//定义了一个接口具体是在NoteEditActivities中实现实现对于便签是否修改的监听
/**
* DataColumnDataColumn
* DataColumn便
* loadNoteData
* 12.22getContentResolver()
* getContentResolverProjection
* 访访
*/
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,//在Note里面定义一个接口类型
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
//这里的用法基本同上方,不过这些接口最后使用在数据库中
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE
};
/**
*
*/
private static final int DATA_ID_COLUMN = 0;
private static final int DATA_CONTENT_COLUMN = 1;
private static final int DATA_MIME_TYPE_COLUMN = 2;
private static final int DATA_MODE_COLUMN = 3;
private static final int NOTE_PARENT_ID_COLUMN = 0;
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
private static final int NOTE_WIDGET_ID_COLUMN = 3;
private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
// New note construct
//初始化一个新的操作的Note
private WorkingFolder(Context context, long folderId) {
mContext = context;
mAlertDate = 0;//这里为什么是0还存疑
mModifiedDate = System.currentTimeMillis();//获取系统时间,保存给最后修改时间
mFolderId = folderId;
mNote = new Note();
mNoteId = 0;
mIsDeleted = false;
mMode = 0;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
//由于传入了noteId,所以是对一个已经存在的便签进行操作。
//由于一些基本的已经定义,所以传入的变量很少。
// Existing note construct
private WorkingFolder(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
loadNote();
}
/**
* @author: zhoushiyu_PickupRAIN
* @methodsName: loadNote
* @description: URI
* @param:
* ->Notes.CONTENT_NOTE_URI:URI
* ->NOTE_PROJECTION :
* ->NOTE_PROJECTION
* @return: String
* @throws: Nopes
*
*/
private void loadNote() {
//定义一个cursor游标用来操作数据。这里传入了很多量如URI、Id、和来自Projection的一系列。
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
//设置一个cursor按行提取各种信息保存到对应的变量中
if (cursor != null) {
if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
}
cursor.close();
} else {
Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
}
loadNoteData();
}
//基本同上一个函数。值得注意的是这里对数据进行了筛选通过selection,还对MIME进行了判断后加载
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
} while (cursor.moveToNext());
}
cursor.close();
} else {
Log.e(TAG, "No data with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
}
//简单的初始化函数,一会看一下用法
public static WorkingFolder createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingFolder note = new WorkingFolder(context, folderId);
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
note.setWidgetType(widgetType);
return note;
}
public static WorkingFolder load(Context context, long id) {
return new WorkingFolder(context, id, 0);
}
//这里涉及了多线程的操作。
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
//由于没有这个便签所以报错并返回false
mNote.syncNote(mContext, mNoteId);//这一行是同步信息的操作
/**
* Update widget content if there exist any widget of this note
*/
//对小组件的各种属性进行判断,来确定是否有组件,进行保存
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
}
/**
*
*
*/
public boolean existInDatabase() {
return mNoteId > 0;
}
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
//这个变量设置是用来记录状态设定是否有发生过更改。
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
}
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
}
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onBackgroundColorChanged();
}
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
//在EditActivities中使用用来设定在list时的状态
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
}
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
}
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
mNote.setTextData(DataColumns.CONTENT, mContent);
}
}
/**
*
*
* 使onOptionsItemSelected NoteEditActivities
*/
//这里的callNote我没有搞懂
public void convertToCallNote(String phoneNumber, long callDate) {
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
}
public void setPassword(String password){
// mPassword=password;
mNote.setNoteValue(NoteColumns.PASSWORD,password);
}
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
public String getContent() {
return mContent;
}
public long getAlertDate() {
return mAlertDate;
}
public long getModifiedDate() {
return mModifiedDate;
}
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
public int getBgColorId() {
return mBgColorId;
}
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
public int getCheckListMode() {
return mMode;
}
public long getNoteId() {
return mNoteId;
}
public long getFolderId() {
return mFolderId;
}
public int getWidgetId() {
return mWidgetId;
}
public int getWidgetType() {
return mWidgetType;
}
public String getPassword() { return mPassword; }
//声明了一个接口具体都在EditActivies中进行实现。主要是一个监听listener来设置各个量的改变
public interface NoteSettingChangedListener {
/**
* Called when the background color of current note has just changed
*/
void onBackgroundColorChanged();
/**
* Called when user set clock
*/
void onClockAlertChanged(long date, boolean set);
/**
* Call when user create note from widget
*/
void onWidgetChanged();
/**
* Call when switch between check list mode and normal mode
* @param oldMode is previous mode before change
* @param newMode is new mode
*/
void onCheckListModeChanged(int oldMode, int newMode);
}
}

@ -31,28 +31,10 @@ import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
/**
*
* @ProjectName: xiaomibianqian
* @Package: model
* @ClassName: WorkingNote
* @Description: 便
* 便便
* NoteEditActivitiesNotesfrom dataNote NoteListActivity SqlData
* NoteSettingChangedListenerNoteEditActivities
*
* @Author: zhoushiyu_br
* @CreateDate: 2023.12.16
* @UpdateUser:
* @UpdateDate: 2023.12.21
* @UpdateRemark:
* @Version: PickupRAIN
*/
public class WorkingNote {
// Note for the working note
private String mPassword;
private Note mNote;//这个在Note里面定义了Note类的基本类型
private Note mNote;
// Note Id
private long mNoteId;
// Note content
@ -62,34 +44,26 @@ public class WorkingNote {
private long mAlertDate;
private long mModifiedDate;//最后的修改日期
private long mModifiedDate;
private int mBgColorId;//定义的颜色的ID
private int mBgColorId;
private int mWidgetId;//定义了一个int类型用来定位到哪一个小组件被使用
private int mWidgetId;
private int mWidgetType;//定义了一个int类型用来区分使用了什么类型的小组件
private int mWidgetType;
private long mFolderId;//用来定位便签放在哪个文件夹
private long mFolderId;
private Context mContext;
public boolean PRIVATE_MODE = false;
private static final String TAG = "WorkingNote"; //定义为静态变量,保证不能再被更改。防止数据出现异常
private static final String TAG = "WorkingNote";
private boolean mIsDeleted; //定义一个布尔变量,用来保存是否要被删除。通过查看用法,发现在后面保存|修改的时候要进行判断。
private boolean mIsDeleted;
private NoteSettingChangedListener mNoteSettingStatusListener;
private NoteSettingChangedListener mNoteSettingStatusListener;//定义了一个接口具体是在NoteEditActivities中实现实现对于便签是否修改的监听
/**
* DataColumnDataColumn
* DataColumn便
* loadNoteData
* 12.22getContentResolver()
* getContentResolverProjection
* 访访
*/
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,//在Note里面定义一个接口类型
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
@ -97,7 +71,7 @@ public class WorkingNote {
DataColumns.DATA3,
DataColumns.DATA4,
};
//这里的用法基本同上方,不过这些接口最后使用在数据库中
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
@ -106,9 +80,7 @@ public class WorkingNote {
NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE
};
/**
*
*/
private static final int DATA_ID_COLUMN = 0;
private static final int DATA_CONTENT_COLUMN = 1;
@ -131,13 +103,12 @@ public class WorkingNote {
//创建新的笔记
// New note construct
//初始化一个新的操作的Note
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;//这里为什么是0还存疑
mModifiedDate = System.currentTimeMillis();//获取系统时间,保存给最后修改时间
mAlertDate = 0;
mModifiedDate = System.currentTimeMillis();
mFolderId = folderId;
mNote = new Note();
mNoteId = 0;
@ -145,8 +116,8 @@ public class WorkingNote {
mMode = 0;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
//由于传入了noteId,所以是对一个已经存在的便签进行操作。
//由于一些基本的已经定义,所以传入的变量很少。
//构建已存在的笔记
// Existing note construct
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
@ -157,24 +128,13 @@ public class WorkingNote {
loadNote();
}
/**
* @author: zhoushiyu_PickupRAIN
* @methodsName: loadNote
* @description: URI
* @param:
* ->Notes.CONTENT_NOTE_URI:URI
* ->NOTE_PROJECTION :
* ->NOTE_PROJECTION
* @return: String
* @throws: Nopes
*
*/
//加载笔记数据
private void loadNote() {
//定义一个cursor游标用来操作数据。这里传入了很多量如URI、Id、和来自Projection的一系列。
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
//设置一个cursor按行提取各种信息保存到对应的变量中
if (cursor != null) {
if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
@ -191,7 +151,8 @@ public class WorkingNote {
}
loadNoteData();
}
//基本同上一个函数。值得注意的是这里对数据进行了筛选通过selection,还对MIME进行了判断后加载
//加载笔记具体数据
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
@ -206,7 +167,6 @@ public class WorkingNote {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
@ -220,7 +180,8 @@ public class WorkingNote {
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
}
//简单的初始化函数,一会看一下用法
//创建新的空白笔记
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
@ -230,10 +191,12 @@ public class WorkingNote {
return note;
}
//加载工作笔记
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
//这里涉及了多线程的操作。
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
@ -242,14 +205,12 @@ public class WorkingNote {
return false;
}
}
//由于没有这个便签所以报错并返回false
mNote.syncNote(mContext, mNoteId);//这一行是同步信息的操作
mNote.syncNote(mContext, mNoteId);
/**
* Update widget content if there exist any widget of this note
*/
//对小组件的各种属性进行判断,来确定是否有组件,进行保存
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
@ -261,10 +222,6 @@ public class WorkingNote {
}
}
/**
*
*
*/
public boolean existInDatabase() {
return mNoteId > 0;
}
@ -277,11 +234,11 @@ public class WorkingNote {
return true;
}
}
//这个变量设置是用来记录状态设定是否有发生过更改。
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
//设置提醒日期
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
@ -309,7 +266,7 @@ public class WorkingNote {
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
//在EditActivities中使用用来设定在list时的状态
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
@ -341,23 +298,14 @@ public class WorkingNote {
}
}
/**
*
*
* 使onOptionsItemSelected NoteEditActivities
*/
//这里的callNote我没有搞懂
//将一条电话通话记录转换为一个笔记
//电话通话记录转换为笔记的功能将电话号码、通话日期和父文件夹ID存储到笔记对象中
// 以便后续可以在应用程序中显示和管理这些通话记录笔记
public void convertToCallNote(String phoneNumber, long callDate) {
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
}
public void setPassword(String password){
// mPassword=password;
mNote.setNoteValue(NoteColumns.PASSWORD,password);
}
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
@ -375,6 +323,7 @@ public class WorkingNote {
return mModifiedDate;
}
//res事件
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
@ -406,9 +355,7 @@ public class WorkingNote {
public int getWidgetType() {
return mWidgetType;
}
public String getPassword() { return mPassword; }
//声明了一个接口具体都在EditActivies中进行实现。主要是一个监听listener来设置各个量的改变
//下面这个类是一个侦听器,用于检测用户是否发生改变背景颜色、设置时钟等操作
public interface NoteSettingChangedListener {
/**
* Called when the background color of current note has just changed

@ -19,7 +19,6 @@ package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
@ -28,9 +27,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
@ -46,7 +43,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
@ -72,7 +68,6 @@ import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -89,6 +84,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
public ImageView ibSetBgColor;
}
//静态Map。用于设置背景选择器点击相关按钮选选择相关颜色。这是选择便签颜色的选择器
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static {
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
@ -158,6 +154,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private String mUserQuery;
private Pattern mPattern;
//创建Activity启动其生命周期
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -174,6 +171,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* Current activity may be killed when the memory is low. Once it is killed, for another time
* user load this activity, we should restore the former state
*/
//Activity状态恢复函数。当Activity因内存不足被杀死后再次加载该活动
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
@ -188,6 +186,19 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
}
//下面是具体执行intent的函数。这个函数会判断接收的intent是干啥的从而执行相关的操作。比如我们的intent是新建便签。定义的CreateNewNote函数
//只是为我们设置好了intent而intent的具体执行还要在initActivityState里。
//对于创建便签他会看是普通的文字便签还是通讯记录对于文字便签新建便签的操作是调用了workingNote.createEmptyNote函数。而这个该函数
//又会调用Note.Note函数。故猜测Note是对应的便签的操作包里面定义了对一个便签的所有操作比如创建等workingNote是Note的一个子集即当前正在被操作的note。
// Notes定义了
//文件夹和便签的顶层即具体样式信息与数据等信息。DataColumn是便签具体的和数据直接相关的NoteColumn是定义了便签或文件夹的其他信息
// 比如用了什么小组件创建时间样式背景颜色等信息。而NoteitemData是一个便签或文件夹的具体实例其中包括了DataColumn及NoteColum等其他信息。
//故对于一个便签来说NoteItemData是他的数据及自身信息实例Note是其操作定义。
//初始化Activity状态函数形参为intent根据传入的intent执行相关活动。
//该函数通过if-else判断intent.getAction()是哪种活动,从而执行相应函数。
//通过intent.getExtra来获取活动执行对象的附加信息。
private boolean initActivityState(Intent intent) {
/**
* If the user specified the {@link Intent#ACTION_VIEW} but not provided with id,
@ -271,27 +282,20 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return true;
}
//回调函数使得Activity与用户互动。这里不断初始化便签界面
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
//初始化便签界面
private void initNoteScreen() {
String defaultText = getIntent().getStringExtra(Notes.INTENT_EXTRA_TEMPLE_TEXT);
if(defaultText!=null){
saveNote();//否则会增加一个null的奇怪现象
mWorkingNote.setWorkingText(mWorkingNote.getContent()+defaultText);
mNoteEditor.setText(mWorkingNote.getContent()); // 将defaultText的值写入到笔记编辑器中
}
// Toast.makeText(getApplicationContext(), defaultText, Toast.LENGTH_SHORT).show();
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent());
} else {
;
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
@ -305,43 +309,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_YEAR));
// String mPassword=mWorkingNote.getPassword();
// if (mWorkingNote.getPassword()!=null){
// final AlertDialog.Builder builder = new AlertDialog.Builder(this);
// View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
// //编辑Text
// final EditText etName = (EditText) view.findViewById(R.id.et_foler_name);
// etName.setText("");
// etName.setHint("请输入密码");
// builder.setTitle("密码检验");
// builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// //获取输入的字符串
// String password = etName.getText().toString();
// Log.d("mPassword: ", mPassword);
// Log.d("password: ", password);
// //判断密码正确性
// if (!password.equals(mPassword)) {
// Log.d("密码不正确,正确密码是:",mPassword);
// dialog.dismiss();
// onBackPressed();
// }
// }
// });
// builder.setNegativeButton("cancel", new DialogInterface.OnClickListener(){
// @Override
// public void onClick(DialogInterface dialog, int which) {
// dialog.dismiss();
// onBackPressed();
// }
// });
// final Dialog dialog = builder.setView(view).show();
// dialog.show();
// }
// else{
// Toast.makeText(NoteEditActivity.this,"还没有设置密码", Toast.LENGTH_SHORT).show();
// }
/**
* TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker
@ -350,6 +317,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
showAlertHeader();
}
//闹钟提醒功能实现。
private void showAlertHeader() {
if (mWorkingNote.hasClockAlert()) {
long time = System.currentTimeMillis();
@ -485,13 +453,14 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (id == R.id.btn_set_bg_color) {
mNoteBgColorSelector.setVisibility(View.VISIBLE);
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
- View.VISIBLE);
View.VISIBLE);
} else if (sBgSelectorBtnsMap.containsKey(id)) {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE);
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
mNoteBgColorSelector.setVisibility(View.GONE);
} else if (sFontSizeBtnsMap.containsKey(id)) {
}
else if (sFontSizeBtnsMap.containsKey(id)) {
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
mFontSizeId = sFontSizeBtnsMap.get(id);
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
@ -595,102 +564,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} else if (itemId == R.id.menu_delete_remind) {
mWorkingNote.setAlertDate(0, false);
}
else if (itemId == R.id.menu_enter_private_mode){
if(mWorkingNote.getPassword()==null){
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);
etName.setText("");
etName.setHint("请输入密码");
builder.setTitle("为便签设置新密码");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = etName.getText().toString();
mWorkingNote.setPassword(password);
if ( mWorkingNote.getPassword() != null ){
Toast.makeText(NoteEditActivity.this,"密码设置成功", Toast.LENGTH_SHORT).show();
}
InputMethodManager inputMethodManager =(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(etName.getWindowToken(),0);
dialog.dismiss();
}
});
builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
InputMethodManager inputMethodManager = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(etName.getWindowToken(),0);
}
});
final Dialog dialog = builder.setView(view).show();
}
} else if (itemId==R.id.menu_select_font) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("选择字体")
.setItems(new CharSequence[]{"默认字体", "仿宋_GB2312", "姚体", "宋体","忍者体","楷体","黑体"}, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
// 创建空的模板
// createEmptyTemplate();
mNoteEditor.setTypeface(Typeface.DEFAULT);
break;
case 1:
// 创建模板 1
// createTemplate("模板 1");
// AssetManager mgr = getAssets();
Typeface tf1=Typeface.createFromAsset(getAssets(),"font/fs.ttf");
mNoteEditor.setTypeface(tf1);
break;
case 2:
Typeface tf2=Typeface.createFromAsset(getAssets(),"font/fzytk.TTF");
mNoteEditor.setTypeface(tf2);
break;
case 3:
// 创建模板 3
// createTemplate("模板 3");
Typeface tf3=Typeface.createFromAsset(getAssets(),"font/stsong.TTF");
mNoteEditor.setTypeface(tf3);
break;
case 4:
Typeface tf4=Typeface.createFromAsset(getAssets(),"font/fzrzt.TTF");
mNoteEditor.setTypeface(tf4);
break;
case 5:
Typeface tf5=Typeface.createFromAsset(getAssets(),"font/kt.ttf");
mNoteEditor.setTypeface(tf5);
break;
case 6:
Typeface tf6=Typeface.createFromAsset(getAssets(),"font/simhei.ttf");
mNoteEditor.setTypeface(tf6);
break;
}
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(NoteEditActivity.this,"字体设置成功", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
});
builder.setNegativeButton("cancle", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(NoteEditActivity.this,"字体设置失败", Toast.LENGTH_SHORT).show();
dialog.dismiss();
} });
final Dialog dialog = builder.show();
}
return true;
}

@ -39,7 +39,7 @@ public class NoteItemData {
NoteColumns.SNIPPET,
NoteColumns.TYPE,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE, NoteColumns.PASSWORD
NoteColumns.WIDGET_TYPE,
};
private static final int ID_COLUMN = 0;
@ -54,7 +54,6 @@ public class NoteItemData {
private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
private static final int PASSWORD_COLUMN = 12;//这里事实上还在存疑,时候需要增加一个列用于索引。
private long mId;
private long mAlertDate;
@ -71,8 +70,6 @@ public class NoteItemData {
private String mName;
private String mPhoneNumber;
private String mPassword;
private boolean mIsLastItem;
private boolean mIsFirstItem;
private boolean mIsOnlyOneItem;
@ -94,8 +91,6 @@ public class NoteItemData {
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mPassword =cursor.getString(PASSWORD_COLUMN);
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
@ -202,9 +197,6 @@ public class NoteItemData {
public int getType() {
return mType;
}
public String getmPassword(){
return mPassword;
}
public int getWidgetType() {
return mWidgetType;
@ -229,11 +221,4 @@ public class NoteItemData {
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
public boolean hasPassword(){
if(mPassword.length()!=0&&mPassword!=null){
return true;
}
return false;
}
}

@ -71,15 +71,18 @@ import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import net.micode.notes.model.WorkingFolder;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Stack;
/**
* NotesListActivty便
*
* main ActivityintentNoteEditActivity
*/
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
@ -115,10 +118,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private TextView mTitleBar;
private long mCurrentFolderId;
private Stack<Long> mFolderIdStack=new Stack<Long>();
private long mCurrentFolderId;
private ContentResolver mContentResolver;
@ -140,6 +141,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_NEW_NODE = 103;
//创建Activity启动其生命周期。完成设置界面样式、初始化资源、且在用户第一次使用App时进行介绍
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -152,6 +154,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
setAppInfoFromRawRes();
}
//intent返回数据接收函数。形参为请求码、结果码、其他intent返回的数据。主要与NoteEditActivity交互。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK
@ -162,6 +165,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
//第一次使用App时调用显示introduction。
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
@ -207,13 +211,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
}
//onCreate调用后回调此Activity对用户可见。
@Override
protected void onStart() {
super.onStart();
startAsyncNotesListQuery();
}
//初始化资源
private void initResources() {
mContentResolver = this.getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());
@ -236,11 +241,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mModeCallBack = new ModeCallback();
}
/**
* ActionMode使便ActionMode
* 便便ActionBar
*/
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu;
private ActionMode mActionMode;
private MenuItem mMoveMenu;
//ActionMode创建函数形参为创建的ActionMode及需要通过该ActionMode显示的Menu对象
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
getMenuInflater().inflate(R.menu.note_list_options, menu);
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
@ -274,6 +284,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true;
}
//用于进行多选(全选)时菜单栏界面的更新
private void updateMenu() {
int selectedCount = mNotesListAdapter.getSelectedCount();
// Update dropdown menu
@ -291,32 +302,39 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
//重新刷新菜单栏时调用.
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
//菜单栏点击动作按钮时调用形参为ActionMode、点击的菜单项.
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
return false;
}
//销毁或退出ActionMode时调用。
public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(false);
mNotesListView.setLongClickable(true);
mAddNewNote.setVisibility(View.VISIBLE);
}
//结束ActionMode时调用
public void finishActionMode() {
mActionMode.finish();
}
//检查当前菜单选择情况(针对多选)并进行菜单更新。
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked) {
mNotesListAdapter.setCheckedItem(position, checked);
updateMenu();
}
//菜单项点击时调用。
public boolean onMenuItemClick(MenuItem item) {
if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
@ -335,7 +353,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();
batchDelete();//批处理删除
}
});
builder.setNegativeButton(android.R.string.cancel, null);
@ -349,61 +367,58 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/**
* 便
*/
private class NewNoteOnTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
Display display = getWindowManager().getDefaultDisplay();
int screenHeight = display.getHeight();
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());
if (view != null && view.getBottom() > start
&& (view.getTop() < (start + 94))) {
mOriginY = (int) event.getY();
mDispatchY = eventY;
event.setLocation(event.getX(), mDispatchY);
mDispatch = true;
return mNotesListView.dispatchTouchEvent(event);
}
}
break;
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
Display display = getWindowManager().getDefaultDisplay();
int screenHeight = display.getHeight();
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();
}
case MotionEvent.ACTION_MOVE: {
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY;
/**
* 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());
if (view != null && view.getBottom() > start
&& (view.getTop() < (start + 94))) {
mOriginY = (int) event.getY();
mDispatchY = eventY;
event.setLocation(event.getX(), mDispatchY);
mDispatch = true;
return mNotesListView.dispatchTouchEvent(event);
}
break;
}
default: {
if (mDispatch) {
event.setLocation(event.getX(), mDispatchY);
mDispatch = false;
return mNotesListView.dispatchTouchEvent(event);
}
break;
} else if (action == MotionEvent.ACTION_MOVE) {
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY;
event.setLocation(event.getX(), mDispatchY);
return mNotesListView.dispatchTouchEvent(event);
}
} else {
if (mDispatch) {
event.setLocation(event.getX(), mDispatchY);
mDispatch = false;
return mNotesListView.dispatchTouchEvent(event);
}
}
return false;
@ -411,6 +426,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
};
/*
便便
*/
private void startAsyncNotesListQuery() {
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION;
@ -420,11 +440,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
/**
*
* (startAsyncNoteListQuery)便
*/
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
//下面就是查询结束后对返回数据的处理,完成界面更新。
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
@ -444,6 +469,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
//通过游标显示文件夹列表菜单,功能为移动便签至某个文件夹
private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(R.string.menu_title_select_folder);
@ -465,25 +492,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
builder.show();
}
//创建新便签。主要是创建intent并启动。
private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
}
private void createTempelNote(int num){
String temple1="Time:\nClass:\nTeacher:\nScore:\n";
String temple2="Group:\nLeader:\nNumber:\nScore:\n";
String temple3="早操:\n早餐\n第一节课\n";
String[] temple= {temple1,temple2,temple2};
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
intent.putExtra(Notes.INTENT_EXTRA_TEMPLE_TEXT,temple[num-1]);
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
}
//批处理删除。用于多个标签删除时.
private void batchDelete() {
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
@ -521,6 +539,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}.execute();
}
//文件夹删除功能。形参为文件夹ID号
private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
@ -548,6 +567,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
//打开便签功能。形参为NoteItemData其为文件夹或便签的实例。
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
@ -555,9 +575,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
//打开文件夹功能
private void openFolder(NoteItemData data) {
Long TempId=new Long(mCurrentFolderId);
mFolderIdStack.add(TempId);
mCurrentFolderId = data.getId();
startAsyncNotesListQuery();
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
@ -574,25 +593,28 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mTitleBar.setVisibility(View.VISIBLE);
}
//视图点击事件。实现创建便签。
public void onClick(View v) {
if (v.getId() == R.id.btn_new_note) {
selectCreatemode(this);
// createNewNote();
createNewNote();
}
}
//显示软键盘
private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
//不显示软键盘
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
//文件夹新建、查看及重命名功能。
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);
@ -642,28 +664,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
});
}
} else if (!TextUtils.isEmpty(name)) {
//如果在根文件夹中创建
if (mCurrentFolderId == Notes.ID_ROOT_FOLDER){
//创建一个ContentValues对象
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
}
//如果在子文件夹中创建
else {
//创建一个ContentValues对象
ContentValues values = new ContentValues();
//将输入的文本添加到ContentValues对象中
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
values.put(NoteColumns.PARENT_ID, String.valueOf(mCurrentFolderId));
//插入新记录
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
}
}
//关闭对话框
dialog.dismiss();
}
});
@ -694,19 +699,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
});
}
//用于处理子Acitivity返回父Activity。相当于对按下“返回箭头”的处理。即返回NotesListActivity活动。
@Override
public void onBackPressed() {
switch (mState) {
case SUB_FOLDER:
mCurrentFolderId = mFolderIdStack.pop().longValue();
//mCurrentFolderId = Notes.ID_ROOT_FOLDER;
if(mCurrentFolderId == Notes.ID_ROOT_FOLDER){
mState = ListEditState.NOTE_LIST;
}
else{
mState = ListEditState.SUB_FOLDER;
}
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery();
mTitleBar.setVisibility(View.GONE);
break;
@ -725,6 +724,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
//更新小组件。
private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
@ -744,7 +744,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
setResult(RESULT_OK, intent);
}
//查看、删除、修改文件夹名称的浮动菜单栏ContextMenu创建
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
//创建ContextMenu形参为创建的菜单、长按事件绑定的视图、视图元素信息
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) {
menu.setHeaderTitle(mFocusNoteDataItem.getSnippet());
@ -755,6 +757,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
};
//下面都是对浮动菜单栏的一些函数
//浮动菜单栏关闭
@Override
public void onContextMenuClosed(Menu menu) {
if (mNotesListView != null) {
@ -762,6 +766,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
super.onContextMenuClosed(menu);
}
//浮动菜单栏点击按钮后事件处理。
@Override
public boolean onContextItemSelected(MenuItem item) {
@ -797,6 +802,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true;
}
//下面是OptionsMenu菜单创建的有关函数。
//OptionsMenu创建
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
@ -815,6 +822,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true;
}
//OptionsMenu点击按钮事件处理
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
@ -842,12 +850,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true;
}
//搜索栏功能实现(就是最上面那个搜索便签得的)
//内部调用了搜索的ui组件
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
return true;
}
//下面使用了异步线程类AsynTask。可在类中可以直接进行UI操作并将后台计算的结果及时的交给UI线程进行UI界面显示。
//将便签内容输出为文本
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() {
@ -894,14 +906,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
//用于跳转到保存的设置界面。这个maybe在isSyncMode==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();
@ -920,7 +937,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|| item.getType() == Notes.TYPE_SYSTEM) {
openFolder(item);
} else if (item.getType() == Notes.TYPE_NOTE) {
testNode(item);
openNode(item);
} else {
Log.e(TAG, "Wrong note type in NOTE_LIST");
}
@ -928,12 +945,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
case SUB_FOLDER:
case CALL_RECORD_FOLDER:
if (item.getType() == Notes.TYPE_NOTE) {
testNode(item);
// openNode(item);
}else if (item.getType() == Notes.TYPE_FOLDER
|| item.getType() == Notes.TYPE_SYSTEM) {
openFolder(item);}
else {
openNode(item);
} else {
Log.e(TAG, "Wrong note type in SUB_FOLDER");
}
break;
@ -945,6 +958,7 @@ 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:
@ -963,6 +977,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
NoteColumns.MODIFIED_DATE + " DESC");
}
//处理长按事件。
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
mFocusNoteDataItem = ((NotesListItem) view).getItemData();
@ -979,99 +994,4 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
return false;
}
public void testNode(final NoteItemData item){
//if (!item.getmPassword().equals("1"))//判断是否有密码,如果没有密码,直接打开即可
if(item.getmPassword()!=null && !item.getmPassword().equals(""))
{
final AlertDialog.Builder builder = new AlertDialog.Builder(this);//初始化对话框
builder.setTitle("验证密码");
View view = LayoutInflater.from(this).inflate(R.layout.pwd, null);//加载对话框的布局文件
final EditText editPwd = (EditText) view.findViewById(R.id.set_password);//初始化对话框里的文本对象
editPwd.setHint("输入隐私密码");
builder.setPositiveButton(android.R.string.ok, null);//确定按钮
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {//取消按钮
hideSoftInput(editPwd);//隐藏软键盘
}
});
showSoftInput();//显示软键盘
final Dialog dialog = builder.setView(view).show();//显示出来
final Button positive = (Button)dialog.findViewById(android.R.id.button1);//加载确定按钮布局文件
positive.setOnClickListener(new OnClickListener() {
public void onClick(View v) {//设置确定按钮的接收器
hideSoftInput(editPwd);//点击确定后收起软键盘
String Password = editPwd.getText().toString();//文本框里的输入值
String mPassword=item.getmPassword();
if (TextUtils.isEmpty(Password) || Password.equals(mPassword)) {//输入不为空
// Toast.makeText(getApplicationContext(), Password, Toast.LENGTH_SHORT).show();
Toast.makeText(NotesListActivity.this,"密码正确", Toast.LENGTH_SHORT).show();
// inputPwd = Password;
openNode(item);
dialog.dismiss();//撤销对话框
}else{
Toast.makeText(NotesListActivity.this,"密码错误", Toast.LENGTH_SHORT).show();
editPwd.setText("");
dialog.show();
}
}
});
if (TextUtils.isEmpty(editPwd.getText())) {//如果文件夹名称为空,则不能选择确定按钮
positive.setEnabled(false);
}
editPwd.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) {//当前文本改变触发,
if (TextUtils.isEmpty(editPwd.getText())) {//文本为空确定按键不可用,不为空则可用
positive.setEnabled(false);
} else {
positive.setEnabled(true);
}
}
public void afterTextChanged(Editable s) {//文本修改之后(未完成)
// TODO Auto-generated method stub
}
});
} else {
openNode(item);
}
}
public void selectCreatemode(Context context){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("选择模板")
.setItems(new CharSequence[]{"创建空新模板", "创建模板 1", "创建模板 2", "创建模板 3"}, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
// 创建空的模板
createNewNote();
break;
case 1:
// 创建模板 1
createTempelNote(1);
// createTemplate("模板 1");
break;
case 2:
// 创建模板 2
createTempelNote(2);
// break;
case 3:
// 创建模板 3
createTempelNote(3);
break;
}
}
})
.show();
}
}

@ -32,7 +32,6 @@ import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
public class NotesListItem extends LinearLayout {
private ImageView mAlert;
private ImageView mLock;
private TextView mTitle;
private TextView mTime;
private TextView mCallName;
@ -85,16 +84,8 @@ public class NotesListItem extends LinearLayout {
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
// if(data.has)
} else {
if(data.getmPassword()==null){
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
}else {
mTitle.setText("便签已加密");
}
//
// mTitle.setText("Jiami");
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/editTextText"
android:layout_width="match_parent"
android:layout_height="182dp"
android:layout_weight="@android:dimen/dialog_min_width_major"
android:ems="20"
android:gravity="center"
android:inputType="text"
android:text="选择你的模版"
android:textSize="50sp"
android:visibility="visible" />
</LinearLayout>

@ -46,12 +46,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/title_alert" />
<!-- <ImageView-->
<!-- android:id="@+id/iv_alert_lock"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_gravity="center_vertical"-->
<!-- android:background="@drawable/lock_private" />-->
<TextView
android:id="@+id/tv_alert_date"

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/set_password"
android:layout_width="fill_parent"
android:hint="@string/hint_foler_name"
android:layout_height="fill_parent" />

@ -49,9 +49,4 @@
<item
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind" />
<item
android:id="@+id/menu_enter_private_mode"
android:title="Enter private mode"/>
</menu>

@ -21,7 +21,4 @@
<item
android:id="@+id/menu_new_note"
android:title="@string/notelist_menu_new"/>
<item
android:id="@+id/menu_new_folder"
android:title="@string/menu_create_folder"/>
</menu>
</menu>

@ -119,6 +119,7 @@
<string name="search">便签</string>
<string name="datetime_dialog_ok">设置</string>
<string name="datetime_dialog_cancel">取消</string>
<plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 条符合“<xliff:g id="SEARCH">%2$s</xliff:g>”的搜索结果</item>
</plurals>

@ -120,6 +120,7 @@
<string name="search">便籤</string>
<string name="datetime_dialog_ok">設置</string>
<string name="datetime_dialog_cancel">取消</string>
<plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 條符合”<xliff:g id="SEARCH">%2$s</xliff:g>“的搜尋結果</item>
</plurals>

@ -142,6 +142,5 @@
<string name="app_already_quit">程序已退出</string>
<string name="Loading">正在登录,你给我等着</string>
<string name="invalid">有人抢银行啦</string>
<string name="font_mode_change">Font mode change</string>
</resources>

@ -66,4 +66,4 @@
<!--<item name="android:displayOptions"/>-->
<item name="android:visibility">visible</item>
</style>
</resources>
</resources>
Loading…
Cancel
Save