From d42f7e0a1ae1cb42f83c64c9df73125ff27897d4 Mon Sep 17 00:00:00 2001 From: pwcv56qjh <1589102979@qq.com> Date: Tue, 18 Apr 2023 21:14:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B2=BE=E8=AF=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notes/data/Contact.java | 78 +++++ src/notes/data/Notes.java | 285 ++++++++++++++++++ src/notes/data/NotesDatabaseHelper.java | 372 ++++++++++++++++++++++++ src/notes/data/NotesProvider.java | 335 +++++++++++++++++++++ src/notes/tool/BackupUtils.java | 348 ++++++++++++++++++++++ src/notes/tool/DataUtils.java | 296 +++++++++++++++++++ src/notes/tool/GTaskStringUtils.java | 114 ++++++++ src/notes/tool/ResourceParser.java | 181 ++++++++++++ 8 files changed, 2009 insertions(+) create mode 100644 src/notes/data/Contact.java create mode 100644 src/notes/data/Notes.java create mode 100644 src/notes/data/NotesDatabaseHelper.java create mode 100644 src/notes/data/NotesProvider.java create mode 100644 src/notes/tool/BackupUtils.java create mode 100644 src/notes/tool/DataUtils.java create mode 100644 src/notes/tool/GTaskStringUtils.java create mode 100644 src/notes/tool/ResourceParser.java diff --git a/src/notes/data/Contact.java b/src/notes/data/Contact.java new file mode 100644 index 0000000..6e27cf5 --- /dev/null +++ b/src/notes/data/Contact.java @@ -0,0 +1,78 @@ +/* + * 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.data; +//以下几个是导入Android自带的几个库文件 +import android.content.Context; +import android.database.Cursor; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Data; +import android.telephony.PhoneNumberUtils; +import android.util.Log;// 安卓日志输出工具类 + +import java.util.HashMap;// HashMap 是一个利用哈希表原理来存储元素的集合,这里用来实现存储联系人列表 + +public class Contact { //联系人 + private static HashMap sContactCache; + private static final String TAG = "Contact";//定义标签 就是类名 是为了方便获取本类名 + + // 定义字符串CALLER_ID_SELECTION + private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + + "(SELECT raw_contact_id " + + " FROM phone_lookup" + + " WHERE min_match = '+')"; + // 获取联系人 + public static String getContact(Context context, String phoneNumber) { + if(sContactCache == null) { + sContactCache = new HashMap(); + } + // 查找HashMap中是否已有phoneNumber信息 + if(sContactCache.containsKey(phoneNumber)) { + return sContactCache.get(phoneNumber); + } + + String selection = CALLER_ID_SELECTION.replace("+", + PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));//toCallerIDMinMatch是安卓自带的号码匹配工具,截取查询号码的后7位作为匹配依据 + + // 查找数据库中phoneNumber的信息 + Cursor cursor = context.getContentResolver().query( + Data.CONTENT_URI, + new String [] { Phone.DISPLAY_NAME }, + selection, + new String[] { phoneNumber }, + null); + + // 判定查询结果 + // moveToFirst()返回第一条 + if (cursor != null && cursor.moveToFirst()) { + try {// 找到相关信息 + String name = cursor.getString(0); + sContactCache.put(phoneNumber, name); + return name;// 异常 + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, " Cursor get string error " + e.toString()); + return null; + } finally { + cursor.close(); // 未找到相关信息 + } + } else { + Log.d(TAG, "No contact matched with number:" + phoneNumber);//联系人中没有匹配到该手机号码的主人 + return null; + } + } +} diff --git a/src/notes/data/Notes.java b/src/notes/data/Notes.java new file mode 100644 index 0000000..3e87e48 --- /dev/null +++ b/src/notes/data/Notes.java @@ -0,0 +1,285 @@ +/* + * 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. + */ +//便签的数据库,该类主要实现了对便签的相关属性包括:Anthority, Tag,以及数据,联系人的信息进行保存管理,其中主要实现了对 +// 若干属性的定义,并创建了DataColumns,DataColumns接口的定义,该两个接口可用于其余类的实现, +// 在便签管理中实现TextNote,CallNote两个类,用于对便签内容的保存。 +package net.micode.notes.data;//package (包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用。 +// 一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。 + +import android.net.Uri;// Notes 类中定义了很多常量,这些常量大多是int型和string型 +public class Notes { + public static final String AUTHORITY = "micode_notes"; + public static final String TAG = "Notes"; + //以下三个常量对NoteColumns.TYPE的值进行设置时会用到 + public static final int TYPE_NOTE = 0; + public static final int TYPE_FOLDER = 1; + public static final int TYPE_SYSTEM = 2; + + /** + * Following IDs are system folders' identifiers + * {@link Notes#ID_ROOT_FOLDER } is default folder + * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder + * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records + */ + public static final int ID_ROOT_FOLDER = 0; + public static final int ID_TEMPARAY_FOLDER = -1;//临时文件夹ID=-1 + public static final int ID_CALL_RECORD_FOLDER = -2; + public static final int ID_TRASH_FOLER = -3; + + public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; + public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; + public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; + 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 int TYPE_WIDGET_INVALIDE = -1; + public static final int TYPE_WIDGET_2X = 0; + public static final int TYPE_WIDGET_4X = 1; + + public static class DataConstants { + public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; + public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; + } + + /** + * Uri to query all notes and folders//查询所有笔记和文件夹的uri + */ + public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");//定义查询便签和文件夹的指针。 + + /** + * Uri to query data + */ + public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");//定义查找数据的指针 + + // 定义NoteColumns的常量,用于后面创建数据库的表头 + public interface NoteColumns { + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + public static final String ID = "_id";//每一行的ID + + /** + * The parent's id for note or folder + *

Type: INTEGER (long)

+ */ + public static final String PARENT_ID = "parent_id";//父节点id的字符串 + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + public static final String CREATED_DATE = "created_date";//创建时间 + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + public static final String MODIFIED_DATE = "modified_date";//最新的更新时间 + + + /** + * Alert date + *

Type: INTEGER (long)

+ */ + public static final String ALERTED_DATE = "alert_date";//提醒时间 + + /** + * Folder's name or text content of note + *

Type: TEXT

+ */ + public static final String SNIPPET = "snippet";//便签的摘要 + + /** + * Note's widget id + *

Type: INTEGER (long)

+ */ + public static final String WIDGET_ID = "widget_id";//小部件的ID + + /** + * Note's widget type + *

Type: INTEGER (long)

+ */ + public static final String WIDGET_TYPE = "widget_type";//小部件的类型 + + /** + * Note's background color's id + *

Type: INTEGER (long)

+ */ + public static final String BG_COLOR_ID = "bg_color_id";//背景颜色 + + /** + * For text note, it doesn't has attachment, for multi-media + * note, it has at least one attachment + *

Type: INTEGER

+ */ + public static final String HAS_ATTACHMENT = "has_attachment";//是否有附件 + + /** + * Folder's count of notes + *

Type: INTEGER (long)

+ */ + public static final String NOTES_COUNT = "notes_count";//文件夹中的便签数量 + + /** + * The file type: folder or note + *

Type: INTEGER

+ */ + public static final String TYPE = "type";//文件类型 + + /** + * The last sync id + *

Type: INTEGER (long)

+ */ + public static final String SYNC_ID = "sync_id";//同步 + + /** + * Sign to indicate local modified or not + *

Type: INTEGER

+ */ + public static final String LOCAL_MODIFIED = "local_modified";//本地信号是否修改 + + /** + * Original parent id before moving into temporary folder + *

Type : INTEGER

+ */ + public static final String ORIGIN_PARENT_ID = "origin_parent_id";//移动到临时文件夹之前的父文件夹 + + /** + * The gtask id + *

Type : TEXT

+ */ + public static final String GTASK_ID = "gtask_id";//后台任务ID + + /** + * The version code + *

Type : INTEGER (long)

+ */ + public static final String VERSION = "version";//版本号 + }//这些常量主要是定义便签的属性的 + + // 定义DataColumns的常量,用于后面创建数据库的表头 + public interface DataColumns { + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + public static final String ID = "_id"; + + /** + * The MIME type of the item represented by this row. + *

Type: Text

+ */ + public static final String MIME_TYPE = "mime_type"; + + /** + * The reference id to note that this data belongs to + *

Type: INTEGER (long)

+ */ + public static final String NOTE_ID = "note_id";//便签ID + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + public static final String CREATED_DATE = "created_date";//创建时间 + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + public static final String MODIFIED_DATE = "modified_date";//修改时间 + + /** + * Data's content + *

Type: TEXT

+ */ + public static final String CONTENT = "content";//内容 + + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + public static final String DATA1 = "data1";//文本内容的数据结构 + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + public static final String DATA2 = "data2";//文本模式 + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA3 = "data3"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA4 = "data4"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA5 = "data5"; + }//主要是定义存储便签内容数据的 + + public static final class TextNote implements DataColumns {//通过接口对数据进行继承 + /** + * Mode to indicate the text in check list mode or not + *

Type: Integer 1:check list mode 0: normal mode

+ */ + public static final String MODE = DATA1; + + public static final int MODE_CHECK_LIST = 1;// 设置为检查列表模式 + + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note";//修改CONTENT_TYPE属性,即内容类型 + + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note";//内容项目的类型 + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");//content 的索引标识符 + }//文本内容的数据结构 + + public static final class CallNote implements DataColumns {//通话数据(CallNote)继承了接口类,而后进行了格式适配 + /** + * Call date for this record + *

Type: INTEGER (long)

0 + */ + public static final String CALL_DATE = DATA1;//存放通话时间 + + /** + * Phone number for this record + *

Type: TEXT

+ */ + public static final String PHONE_NUMBER = DATA3;//存放通话号码信息 + + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; + + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");//内容标识符 + }//电话内容的数据结构 +} diff --git a/src/notes/data/NotesDatabaseHelper.java b/src/notes/data/NotesDatabaseHelper.java new file mode 100644 index 0000000..9b78d28 --- /dev/null +++ b/src/notes/data/NotesDatabaseHelper.java @@ -0,0 +1,372 @@ +//用于存储Notes的数据,以及根据数据更改Notes结构体的变量 +/* + * 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.data;//声明包 + +import android.content.ContentValues;//就是用于保存一些数据(string boolean byte double float int long short ...)信息,这些信息可以被数据库操作时使用。 +import android.content.Context;//加载和访问资源。(android中主要是这两个功能,但是这里具体不清楚) +import android.database.sqlite.SQLiteDatabase;//主要提供了对应于添加、删除、更新、查询的操作方法: insert()、delete()、update()和query()。配合content.values +import android.database.sqlite.SQLiteOpenHelper;//用来管理数据的创建和版本更新 +import android.util.Log; + +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +//数据库操作,用SQLOpenhelper,对一些note和文件进行数据库的操作,比如删除文件后,将文件里的note也相应删除 + +public class NotesDatabaseHelper extends SQLiteOpenHelper { + private static final String DB_NAME = "note.db"; + + private static final int DB_VERSION = 4; + + public interface TABLE {//接口,分成note和data,在后面的程序里分别使用过 + public static final String NOTE = "note"; + + public static final String DATA = "data"; + } + + private static final String TAG = "NotesDatabaseHelper"; + + private static NotesDatabaseHelper mInstance; + + private static final String CREATE_NOTE_TABLE_SQL = + "CREATE TABLE " + TABLE.NOTE + "(" + + NoteColumns.ID + " INTEGER PRIMARY KEY," + + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + + 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" + + ")";//创建一个note时,数据库中需要存储的项目的名称,就相当于创建一个表格的表头的内容。 + + private static final String CREATE_DATA_TABLE_SQL = + "CREATE TABLE " + TABLE.DATA + "(" + + DataColumns.ID + " INTEGER PRIMARY KEY," + + DataColumns.MIME_TYPE + " TEXT NOT NULL," + + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA1 + " INTEGER," + + DataColumns.DATA2 + " INTEGER," + + DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + + ")";//和上面的功能一样,主要是存储的项目不同 + + private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = + "CREATE INDEX IF NOT EXISTS note_id_index ON " + + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";//存储便签编号的一个数据表格 + + /** + * Increase folder's note count when move note to the folder + */ + private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = + "CREATE TRIGGER increase_folder_count_on_update "+ + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END";//在文件夹中移入一个Note之后需要更改的数据的表格 + + /** + * Decrease folder's note count when move note from folder + */ + private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = + "CREATE TRIGGER decrease_folder_count_on_update " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + + " END";//在文件夹中移出一个Note之后需要更改的数据的表格 + + /** + * Increase folder's note count when insert new note to the folder + */ + private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = + "CREATE TRIGGER increase_folder_count_on_insert " + + " AFTER INSERT ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END";//在文件夹中插入一个Note之后需要更改的数据的表格 + + /** + * Decrease folder's note count when delete note from the folder + */ + private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = + "CREATE TRIGGER decrease_folder_count_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0;" + + " END";//在文件夹中删除一个Note之后需要更改的数据的表格 + + /** + * Update note's content when insert data with type {@link DataConstants#NOTE} + */ + private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = + "CREATE TRIGGER update_note_content_on_insert " + + " AFTER INSERT ON " + TABLE.DATA + + " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + + " END";//在文件夹中对一个Note导入新的数据之后需要更改的数据的表格 + + /** + * Update note's content when data with {@link DataConstants#NOTE} type has changed + */ + private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = + "CREATE TRIGGER update_note_content_on_update " + + " AFTER UPDATE ON " + TABLE.DATA + + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + + " END";//Note数据被修改后需要更改的数据的表格 + + /** + * Update note's content when data with {@link DataConstants#NOTE} type has deleted + */ + private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = + "CREATE TRIGGER update_note_content_on_delete " + + " AFTER delete ON " + TABLE.DATA + + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=''" + + " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + + " END";//Note数据被删除后需要更改的数据的表格 + + /** + * Delete datas belong to note which has been deleted + */ + private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = + "CREATE TRIGGER delete_data_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.DATA + + " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + + " END";//删除已删除的便签的数据后需要更改的数据的表格 + + /** + * Delete notes belong to folder which has been deleted + */ + private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = + "CREATE TRIGGER folder_delete_notes_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END";//删除已删除的文件夹的便签后需要更改的数据的表格 + + /** + * Move notes belong to folder which has been moved to trash folder + */ + private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = + "CREATE TRIGGER folder_move_notes_on_trash " + + " AFTER UPDATE ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END";//还原垃圾桶中便签后需要更改的数据的表格 + + public NotesDatabaseHelper(Context context) { + super(context, DB_NAME, null, DB_VERSION); + }//构造函数,传入数据库的名称和版本 + + public void createNoteTable(SQLiteDatabase db) { + db.execSQL(CREATE_NOTE_TABLE_SQL); + reCreateNoteTableTriggers(db); + createSystemFolder(db); + Log.d(TAG, "note table has been created"); + }//创建表格(用来存储标签属性) + + private void reCreateNoteTableTriggers(SQLiteDatabase db) { + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); + + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER); + db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER); + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER); + db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); + db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); + }//execSQL是数据库操作的API,主要是更改行为的SQL语句。 + //在这里主要是用来重新创建上述定义的表格用的,先删除原来有的数据库的触发器再重新创建新的数据库 + + + private void createSystemFolder(SQLiteDatabase db) { + ContentValues values = new ContentValues(); + + /** + * call record foler for call notes + */ + //储存呼叫记录的文件夹 + values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * root folder which is default folder + */ + //设置根目录为默认文件夹 + values.clear(); + values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * temporary folder which is used for moving note + */ + //设置临时文件夹作为文件移动的有效目标 + values.clear(); + values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * create trash folder + */ + //创建回收站 + values.clear(); + values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + }//创建几个系统文件夹 + + public void createDataTable(SQLiteDatabase db) { + db.execSQL(CREATE_DATA_TABLE_SQL); + reCreateDataTableTriggers(db); + db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); + Log.d(TAG, "data table has been created"); + }//创建表格(用来存储标签内容) + + private void reCreateDataTableTriggers(SQLiteDatabase db) { + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); + + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER); + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); + }//同上面的execSQL + + static synchronized NotesDatabaseHelper getInstance(Context context) { + if (mInstance == null) { + mInstance = new NotesDatabaseHelper(context); + } + return mInstance; + }//上网查是为解决同一时刻只能有一个线程执行. + //在写程序库代码时,有时有一个类需要被所有的其它类使用, + //但又要求这个类只能被实例化一次,是个服务类,定义一次,其它类使用同一个这个类的实例 + + @Override + public void onCreate(SQLiteDatabase db) { + createNoteTable(db); + createDataTable(db); + }//实现两个表格(上面创建的两个表格) + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {//更新数据库 + // 提供了onCreate()、onUpgrade()两个回调函数,允许我们再创建和升级数据库时,进行自己的操作 + boolean reCreateTriggers = false;//是否重建 + boolean skipV2 = false;//是否从V2升级到V3 + + if (oldVersion == 1) {//从V1升级到V2 + upgradeToV2(db); + skipV2 = true; // this upgrade including the upgrade from v2 to v3 + oldVersion++; + } + + if (oldVersion == 2 && !skipV2) {//从V2升级到V3 + upgradeToV3(db); + reCreateTriggers = true; + oldVersion++; + } + + if (oldVersion == 3) {//从V3升级到V4 + upgradeToV4(db); + oldVersion++; + } + + if (reCreateTriggers) {//如果重新创建,创建新的note table和datatable + reCreateNoteTableTriggers(db); + reCreateDataTableTriggers(db); + } + + if (oldVersion != newVersion) {//判断是否版本升级成功,若版本号没有升级到相应new版本,这个就会抛出异常 + throw new IllegalStateException("Upgrade notes database to version " + newVersion + + "fails"); + } + }//数据库版本的更新(数据库内容的更改) + + private void upgradeToV2(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); + db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); + createNoteTable(db); + createDataTable(db); + }//更新到V2版本 + + private void upgradeToV3(SQLiteDatabase db) { + // drop unused triggers + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); + // add a column for gtask id + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + + " TEXT NOT NULL DEFAULT ''"); + // add a trash system folder + ContentValues values = new ContentValues(); + values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + }//更新到V3版本 + + private void upgradeToV4(SQLiteDatabase db) { + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + + " INTEGER NOT NULL DEFAULT 0"); + }//更新到V4版本 +} diff --git a/src/notes/data/NotesProvider.java b/src/notes/data/NotesProvider.java new file mode 100644 index 0000000..08e3a2c --- /dev/null +++ b/src/notes/data/NotesProvider.java @@ -0,0 +1,335 @@ +/* + * 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.data;//声明所属包名 + +//引用android自带的类,包括数据库等 +import android.app.SearchManager; +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Intent; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper.TABLE; + +//ContentProvider提供的方法 +//query:查询 +//insert:插入 +//update:更新 +//delete:删除 +//getType:得到数据类型 + +public class NotesProvider extends ContentProvider {//为存储和获取数据提供接口。可以在不同的应用程序之间共享数据 + + private static final UriMatcher mMatcher;// UriMatcher用于匹配Uri + + private NotesDatabaseHelper mHelper;//数据库助手实例化 + + private static final String TAG = "NotesProvider";//给部分变量赋值 + + private static final int URI_NOTE = 1; + private static final int URI_NOTE_ITEM = 2; + private static final int URI_DATA = 3; + private static final int URI_DATA_ITEM = 4; + + private static final int URI_SEARCH = 5; + private static final int URI_SEARCH_SUGGEST = 6; + + static { + // 创建UriMatcher时,调用UriMatcher(UriMatcher.NO_MATCH)表示不匹配任何路径的返回码 + mMatcher = new UriMatcher(UriMatcher.NO_MATCH); + // 把需要匹配Uri路径全部给注册上 + mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); + mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); + mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); + mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); + mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); + } + + /** + * x'0A' represents the '\n' character in sqlite. For title and content in the search result, + * we will trim '\n' and white space in order to show more information. + */ + // 声明 NOTES_SEARCH_PROJECTION + private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + // 声明NOTES_SNIPPET_SEARCH_QUERY + private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + + @Override + // Context只有在onCreate()中才被初始化 + // 对mHelper进行实例化 + public boolean onCreate() { + mHelper = NotesDatabaseHelper.getInstance(getContext()); + return true; + } + + @Override + // 查询uri在数据库中对应的位置 + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + Cursor c = null; + // 获取可读数据库 + SQLiteDatabase db = mHelper.getReadableDatabase(); + String id = null; + // 匹配查找uri + switch (mMatcher.match(uri)) { + // 对于不同的匹配值,在数据库中查找相应的条目 + case URI_NOTE://查询便签 + c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, + sortOrder); + break; + case URI_NOTE_ITEM://查询便签条目 + id = uri.getPathSegments().get(1); + c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + + parseSelection(selection), selectionArgs, null, null, sortOrder); + break; + case URI_DATA://查询数据 + c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, + sortOrder); + break; + case URI_DATA_ITEM://查询id对应的具体数据 + id = uri.getPathSegments().get(1); + c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + + parseSelection(selection), selectionArgs, null, null, sortOrder); + break; + case URI_SEARCH://匹配到搜索 + case URI_SEARCH_SUGGEST: + if (sortOrder != null || projection != null) { + // 不合法的参数异常 + throw new IllegalArgumentException( + "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); + } + + String searchString = null; + if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { + if (uri.getPathSegments().size() > 1) { + // getPathSegments()方法得到一个String的List, + // 在uri.getPathSegments().get(1)为第2个元素 + searchString = uri.getPathSegments().get(1); + } + } else { + searchString = uri.getQueryParameter("pattern"); + } + + if (TextUtils.isEmpty(searchString)) { + return null; + } + + try { + searchString = String.format("%%%s%%", searchString); + c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, + new String[] { searchString }); + } catch (IllegalStateException ex) { + Log.e(TAG, "got exception: " + ex.toString()); + } + break; + default: + // 抛出异常 + throw new IllegalArgumentException("Unknown URI " + uri); + } + if (c != null) { + c.setNotificationUri(getContext().getContentResolver(), uri); + } + return c; + } + + @Override + // 插入一个uri + public Uri insert(Uri uri, ContentValues values) { + // 获得可写的数据库 + SQLiteDatabase db = mHelper.getWritableDatabase(); + long dataId = 0, noteId = 0, insertedId = 0; + switch (mMatcher.match(uri)) { + // 新增一个条目 + case URI_NOTE: + insertedId = noteId = db.insert(TABLE.NOTE, null, values); + break; + // 如果是数据类型,查找NOTE_ID + case URI_DATA: + if (values.containsKey(DataColumns.NOTE_ID)) { + noteId = values.getAsLong(DataColumns.NOTE_ID); + } else {//错误的数据格式,没有note的id + Log.d(TAG, "Wrong data format without note id:" + values.toString()); + } + insertedId = dataId = db.insert(TABLE.DATA, null, values);//把便签数据插入到数据库 + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri);//抛出异常:未知的URI + } + // Notify the note uri + // notifyChange获得一个ContextResolver对象并且更新里面的内容 + if (noteId > 0) { + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); + } + + // Notify the data uri + if (dataId > 0) { + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); + } + // 返回插入的uri的路径 + return ContentUris.withAppendedId(uri, insertedId); + } + + @Override + // 删除一个uri + public int delete(Uri uri, String selection, String[] selectionArgs) { + //Uri代表要操作的数据,Android上可用的每种资源 -包括 图像、视频片段、音频资源等都可以用Uri来表示。 + int count = 0; + String id = null; + // 获得可写的数据库 + SQLiteDatabase db = mHelper.getWritableDatabase(); + boolean deleteData = false; + switch (mMatcher.match(uri)) { + case URI_NOTE: + selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; + count = db.delete(TABLE.NOTE, selection, selectionArgs); + break; + case URI_NOTE_ITEM: + id = uri.getPathSegments().get(1); + /** + * ID that smaller than 0 is system folder which is not allowed to + * trash + */ + long noteId = Long.valueOf(id); + if (noteId <= 0) { + break; + } + count = db.delete(TABLE.NOTE, + NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + break; + case URI_DATA: + count = db.delete(TABLE.DATA, selection, selectionArgs);//根据选择条件删除 + deleteData = true; + break; + case URI_DATA_ITEM: + id = uri.getPathSegments().get(1); + count = db.delete(TABLE.DATA, + DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + deleteData = true; + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + if (count > 0) { + if (deleteData) { + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); + } + getContext().getContentResolver().notifyChange(uri, null);//对所有修改进行通知 + } + return count; + } + + @Override + // 更新一个uri + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + int count = 0; + String id = null; + SQLiteDatabase db = mHelper.getWritableDatabase();//获取可写的数据库 + boolean updateData = false; + switch (mMatcher.match(uri)) { + case URI_NOTE: + increaseNoteVersion(-1, selection, selectionArgs); + count = db.update(TABLE.NOTE, values, selection, selectionArgs); + break; + case URI_NOTE_ITEM: + id = uri.getPathSegments().get(1); + increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); + count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + + parseSelection(selection), selectionArgs); + break; + case URI_DATA: + count = db.update(TABLE.DATA, values, selection, selectionArgs); + updateData = true; + break; + case URI_DATA_ITEM: + id = uri.getPathSegments().get(1); + count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + + parseSelection(selection), selectionArgs); + updateData = true; + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + if (count > 0) { + if (updateData) { + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); + } + getContext().getContentResolver().notifyChange(uri, null); + } + return count; + } + // 将字符串解析成规定格式 + private String parseSelection(String selection) {//将字符串解析成规定格式 + return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); + } + //增加一个noteVersion + private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { + StringBuilder sql = new StringBuilder(120); + sql.append("UPDATE "); + sql.append(TABLE.NOTE); + sql.append(" SET "); + sql.append(NoteColumns.VERSION); + sql.append("=" + NoteColumns.VERSION + "+1 "); + + if (id > 0 || !TextUtils.isEmpty(selection)) {//selection非空,或ID>0,添加WHERE + sql.append(" WHERE "); + } + if (id > 0) {//如果id>0,添加id + sql.append(NoteColumns.ID + "=" + String.valueOf(id)); + } + if (!TextUtils.isEmpty(selection)) {//输入的文本非空的条件下输入到数据库中 + String selectString = id > 0 ? parseSelection(selection) : selection; + for (String args : selectionArgs) { + selectString = selectString.replaceFirst("\\?", args); + } + sql.append(selectString); + } + + // execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句 + mHelper.getWritableDatabase().execSQL(sql.toString()); + } + + @Override + public String getType(Uri uri) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/notes/tool/BackupUtils.java b/src/notes/tool/BackupUtils.java new file mode 100644 index 0000000..a1573c0 --- /dev/null +++ b/src/notes/tool/BackupUtils.java @@ -0,0 +1,348 @@ +/* + * 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. + */ +//BackupUtils 是一个备份工具类,用于数据备份读取和显示 +package net.micode.notes.tool;//定义小米便签类:功能类 + +//调用了Android的包 +import android.content.Context; +import android.database.Cursor; +import android.os.Environment; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + + +public class BackupUtils { + private static final String TAG = "BackupUtils"; + // Singleton stuff + private static BackupUtils sInstance; + + public static synchronized BackupUtils getInstance(Context context) { + //ynchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A) + //运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。 + //它包括两种用法:synchronized 方法和 synchronized 块 + if (sInstance == null) {//如果当前备份不存在,则新声明一个 + sInstance = new BackupUtils(context); + } + return sInstance; + } + + /** + * Following states are signs to represents backup or restore + * status + */ + // Currently, the sdcard is not mounted SD卡没有被装入手机 + public static final int STATE_SD_CARD_UNMOUONTED = 0; + // The backup file not exist 备份文件夹不存在 + public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; + // The data is not well formated, may be changed by other programs 数据已被破坏,可能被修改 + public static final int STATE_DATA_DESTROIED = 2; + // Some run-time exception which causes restore or backup fails 超时异常 + public static final int STATE_SYSTEM_ERROR = 3; + // Backup or restore success 成功存储 + public static final int STATE_SUCCESS = 4; + + private TextExport mTextExport; + + private BackupUtils(Context context) { + mTextExport = new TextExport(context); + }//初始化函数 + + private static boolean externalStorageAvailable() {//外部存储功能是否可用 + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + } + + public int exportToText() { + return mTextExport.exportToText(); + }//输出至文本 + + public String getExportedTextFileName() { + return mTextExport.mFileName; + }//获取输出的文本文件名 + + public String getExportedTextFileDir() { + return mTextExport.mFileDirectory; + }//获得输出文本的文本路径 + + private static class TextExport {//文本输出类。包含笔记ID、修改日期等数据及其格式 + private static final String[] NOTE_PROJECTION = {//定义了一个数组储存便签的信息 + NoteColumns.ID, + NoteColumns.MODIFIED_DATE, + NoteColumns.SNIPPET, + NoteColumns.TYPE + }; + + private static final int NOTE_COLUMN_ID = 0;//初始化便签ID + + private static final int NOTE_COLUMN_MODIFIED_DATE = 1;//初始化修改时间 + + private static final int NOTE_COLUMN_SNIPPET = 2;//初始化数据标识 + + private static final String[] DATA_PROJECTION = {//定义字符串存储数据的基本信息 + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + //标识设定:数据内容标识为0;媒体类型标识为1;访问日期标识为2;电话号码标识为4; + private static final int DATA_COLUMN_CONTENT = 0; + + private static final int DATA_COLUMN_MIME_TYPE = 1; + + private static final int DATA_COLUMN_CALL_DATE = 2; + + private static final int DATA_COLUMN_PHONE_NUMBER = 4; +//文档格式标识:名称为0;日期为1;内容为2 + private final String [] TEXT_FORMAT; + private static final int FORMAT_FOLDER_NAME = 0; + private static final int FORMAT_NOTE_DATE = 1; + private static final int FORMAT_NOTE_CONTENT = 2; + + private Context mContext;//为该类定义一个内部上下类 + private String mFileName;//定义文件名 + private String mFileDirectory;//定义文件夹字符串 + + public TextExport(Context context) {//从context类实例中获取信息,给对应的属性赋初始值 + TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); + mContext = context; + mFileName = ""; + mFileDirectory = ""; + } + + private String getFormat(int id) { + return TEXT_FORMAT[id]; + }//通过ID返回文件的格式信息 + + /** + * Export the folder identified by folder id to text + */ + private void exportFolderToText(String folderId, PrintStream ps) {//通过文件夹ID将目录导出后成文件 + // Query notes belong to this folder + Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { + folderId + }, null); + + if (notesCursor != null) {//利用光标来扫描内容,区别为callnote和note两种,靠ps.printline输出 + if (notesCursor.moveToFirst()) { + do { + // Print note's last modified date ps里面保存有这份note的日期 + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = notesCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); //将文件导出到text + } while (notesCursor.moveToNext()); + } + notesCursor.close(); + } + } + + /** + * Export note identified by id to a print stream + */ + private void exportNoteToText(String noteId, PrintStream ps) { + Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, + DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { + noteId + }, null); + + if (dataCursor != null) { //利用光标来扫描内容,区别为callnote和note两种,靠ps.printline输出 + if (dataCursor.moveToFirst()) { + do { + String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); + if (DataConstants.CALL_NOTE.equals(mimeType)) { + // Print phone number + String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); + long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); + String location = dataCursor.getString(DATA_COLUMN_CONTENT); + + if (!TextUtils.isEmpty(phoneNumber)) {//判断是否为空字符 + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + phoneNumber)); + } + // Print call date + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat + .format(mContext.getString(R.string.format_datetime_mdhm), + callDate))); + // Print call attachment location + if (!TextUtils.isEmpty(location)) {//判断是否存在位置信息,若存在就打印位置信息 + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + location)); + } + } else if (DataConstants.NOTE.equals(mimeType)) {//如果只有便签的内容且存在,就将其输出 + String content = dataCursor.getString(DATA_COLUMN_CONTENT); + if (!TextUtils.isEmpty(content)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + content)); + } + } + } while (dataCursor.moveToNext()); + } + dataCursor.close();//关闭游标 + } + // print a line separator between note + try { + ps.write(new byte[] { + Character.LINE_SEPARATOR, Character.LETTER_NUMBER// 在Note下方输出一条线 + }); + } catch (IOException e) { + Log.e(TAG, e.toString());//检测异常,如果有异常输出红色TAG + } + } + + /** + * Note will be exported as text which is user readable + */ + public int exportToText() {//以TEXT形式输出到外部设备 + if (!externalStorageAvailable()) {//检查外部设备是否安装好,没有的话则输出显示错误的信息。 + Log.d(TAG, "Media was not mounted"); + return STATE_SD_CARD_UNMOUONTED; + } + + PrintStream ps = getExportToTextPrintStream(); + if (ps == null) {//获得外部设备存储路径 + Log.e(TAG, "get print stream error"); + return STATE_SYSTEM_ERROR; + } + // First export folder and its notes + Cursor folderCursor = mContext.getContentResolver().query(//定位需要导出的文件夹 + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); + + if (folderCursor != null) {//导出文件夹,把里面的便签导出来 + if (folderCursor.moveToFirst()) { + do { + // Print folder's name + String folderName = ""; + if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { + folderName = mContext.getString(R.string.call_record_folder_name); + } else { + folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); + } + if (!TextUtils.isEmpty(folderName)) {//判断文件夹名称是否为空,不为空则输出格式和名称 + ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); + } + String folderId = folderCursor.getString(NOTE_COLUMN_ID);// 通过便签ID得到folderID + exportFolderToText(folderId, ps); + } while (folderCursor.moveToNext()); + } + folderCursor.close(); + } + + // Export notes in root's folder + Cursor noteCursor = mContext.getContentResolver().query(//将根目录里的便签导出 + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + + "=0", null, null); + + if (noteCursor != null) { + if (noteCursor.moveToFirst()) { + do {//将便签的修改日期显示在屏幕上 + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = noteCursor.getString(NOTE_COLUMN_ID);//找到这块数据的ID + exportNoteToText(noteId, ps); + } while (noteCursor.moveToNext());//光标下移 + } + noteCursor.close(); + } + ps.close(); + + return STATE_SUCCESS; + } + + /** + * Get a print stream pointed to the file {@generateExportedTextFile} + */ + private PrintStream getExportToTextPrintStream() {//获取指向文件的打印流 + File file = generateFileMountedOnSDcard(mContext, R.string.file_path,//初始化存储在SD卡的文件 + R.string.file_name_txt_format); + if (file == null) {//如果文件为空,则创建失败 + Log.e(TAG, "create file to exported failed"); + return null; + } + mFileName = file.getName();//获得文件名 + mFileDirectory = mContext.getString(R.string.file_path);//文件输出流及异常处理 + PrintStream ps = null; + try {//将ps输出流输出到特定的文件,目的就是导出到文件,而不是直接输出 + FileOutputStream fos = new FileOutputStream(file); + ps = new PrintStream(fos); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } catch (NullPointerException e) { + e.printStackTrace(); + return null; + } + return ps; + } + } + + /** + * Generate the text file to store imported data + */ + private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {//生成存储文件,安装在SD卡上 + StringBuilder sb = new StringBuilder();///构建一个动态字符串将外部存储器路径、文件路径、编辑时间加入到其中 + sb.append(Environment.getExternalStorageDirectory());//外部(SD卡)的存储路径 + sb.append(context.getString(filePathResId));//文件的存储路径 + File filedir = new File(sb.toString()); + sb.append(context.getString( + fileNameFormatResId, + DateFormat.format(context.getString(R.string.format_date_ymd),//将当前的系统时间以预定的格式输出 + System.currentTimeMillis()))); + File file = new File(sb.toString());//将输出连接到一个文件里 + + try {//如果这些文件不存在,则新建 + if (!filedir.exists()) { + filedir.mkdir(); + } + if (!file.exists()) { + file.createNewFile(); + } + return file; + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IOException e) {//输入输出异常处理 + e.printStackTrace(); + } + + return null; + } +} + + diff --git a/src/notes/tool/DataUtils.java b/src/notes/tool/DataUtils.java new file mode 100644 index 0000000..4f495a4 --- /dev/null +++ b/src/notes/tool/DataUtils.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//对数据处理的操作定义 +package net.micode.notes.tool; +//导入相关的库 +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentResolver;//批量删除笔记 +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.OperationApplicationException;//操作方法 +import android.database.Cursor; +import android.os.RemoteException;//远程交互 +import android.util.Log; + +import net.micode.notes.data.Notes;//便签数据类相关的包 +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.NoteColumns;//便签栏的数据 +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; + +import java.util.ArrayList;//导入数组处理包 +import java.util.HashSet;//导入需要的包 + + +public class DataUtils {//数据的集成工具类 + public static final String TAG = "DataUtils"; + public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) {//实现了批量删除便签 + if (ids == null) {//判断笔记id是否为空 + Log.d(TAG, "the ids is null"); + return true; + } + if (ids.size() == 0) { //判断笔记内容是否为空 + Log.d(TAG, "no id is in the hashset"); + return true; + } + + ArrayList operationList = new ArrayList();//提供一个事件的列表 + for (long id : ids) {//遍历数据,如果此数据为根目录则跳过此数据不删除,如果不是根目录则将此数据删除 + if(id == Notes.ID_ROOT_FOLDER) {//避免出现删除系统目录的情况 + Log.e(TAG, "Don't delete system folder root"); + continue; + } + ContentProviderOperation.Builder builder = ContentProviderOperation + .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));//newDelete用来实现删除 + operationList.add(builder.build());//将操作添加至列表 + } + try {//返回被删除的数据,如果返回为空则删除失败返回false,打印异常信息,删除成功返回true + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + if (results == null || results.length == 0 || results[0] == null) { + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + return false; + } + return true; + } catch (RemoteException e) {//对错误进行处理,并将错误存储到日志当中 + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } + return false; + } + + public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {//用来把某一个便签移动到某一个文件夹 + ContentValues values = new ContentValues(); + values.put(NoteColumns.PARENT_ID, desFolderId);//将PARENT_ID更改为目标目录ID + values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); + values.put(NoteColumns.LOCAL_MODIFIED, 1);//设置修改符号为1 + resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);//对移动过的便签进行数据的更新 + } + + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, + long folderId) {//批量的将标签移动到另一个目录下 + if (ids == null) { + Log.d(TAG, "the ids is null"); + return true; + } + + ArrayList operationList = new ArrayList(); + for (long id : ids) {//将ids里包含的每一列的数据逐次加入到operationList中,等待最后的批量处理 + ContentProviderOperation.Builder builder = ContentProviderOperation + .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + builder.withValue(NoteColumns.PARENT_ID, folderId); + builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); + operationList.add(builder.build()); + } + + try {//同上的容错机制,对于一些异常进行处理与汇报 + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);//调用applybatch一次性处理一个操作列表 + if (results == null || results.length == 0 || results[0] == null) { + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + return false; + } + return true; + } catch (RemoteException e) {//异常处理 + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } + return false; + } + + /** + * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + */ + public static int getUserFolderCount(ContentResolver resolver) {//获取用户文件夹数 + Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, + null);//String.valueof将形参转成字符串返回 + + int count = 0; + if(cursor != null) { + if(cursor.moveToFirst()) { + try { + count = cursor.getInt(0); + } catch (IndexOutOfBoundsException e) {// 索引序号超出界限 + Log.e(TAG, "get folder count failed:" + e.toString()); + } finally { + cursor.close();//关闭游标 + } + } + } + return count; + } + + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {//是否在便签数据库中可见 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null,//通过withappendedid的方法为uri加上id + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, + new String [] {String.valueOf(type)},//表示筛选出列中type等于string数组中type,且每一项的PARENT_ID不等于Notes.ID.TRAXH_FOLDER + null); + + boolean exist = false;//查询文件 + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {//判断该note是否在数据库中存在 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, null, null, null); + + boolean exist = false;//初始化存在状态 + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {//检查文件名字是否可见 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), + null, null, null, null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {//通过名字查询文件是否存在 + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.SNIPPET + "=?", + new String[] { name }, null);//筛选出type相同,并且未被删除,名字对的上的 + boolean exist = false; + if(cursor != null) { + if(cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) {/使用hashset来存储不同窗口的id和type并且建立对应关系 + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, + new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(folderId) }, + null);//查询条件是,父ID是否为传入的folderId + + HashSet set = null;//根据窗口的记录一一添加对应的属性值 + if (c != null) { + if (c.moveToFirst()) { + set = new HashSet(); + do { + try {//把每一个条目对应的窗口id和type记录下来,放到set里面。每一行的第0个int和第1个int分别对应widgetId和widgetType + AppWidgetAttribute widget = new AppWidgetAttribute(); + widget.widgetId = c.getInt(0); + widget.widgetType = c.getInt(1); + set.add(widget); + } catch (IndexOutOfBoundsException e) {//当下标超过边界,那么返回错误 + Log.e(TAG, e.toString()); + } + } while (c.moveToNext()); + } + c.close(); + } + return set; + } + + public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {//通过笔记ID获取号码 + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.PHONE_NUMBER }, + CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", + new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, + null);//新建字符列表 + + if (cursor != null && cursor.moveToFirst()) {//获取电话号码,并处理异常。 + try {//返回电话号码 + return cursor.getString(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call number fails " + e.toString()); + } finally { + cursor.close(); + } + } + return ""; + } + + public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {//获取ID通过电话号码和呼叫日期 + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.NOTE_ID }, + CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" + + CallNote.PHONE_NUMBER + ",?)", + new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, + null);//通过数据库操作,查询条件是(callDate和phoneNumber匹配传入参数的值) + + if (cursor != null) { + if (cursor.moveToFirst()) { + try { + return cursor.getLong(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call note id fails " + e.toString()); + } + } + cursor.close(); + } + return 0; + } + + public static String getSnippetById(ContentResolver resolver, long noteId) {//按ID获取片段 + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, + new String [] { NoteColumns.SNIPPET }, + NoteColumns.ID + "=?", + new String [] { String.valueOf(noteId)}, + null); + + if (cursor != null) { + String snippet = ""; + if (cursor.moveToFirst()) { + snippet = cursor.getString(0); + } + cursor.close(); + return snippet; + } + throw new IllegalArgumentException("Note is not found with id: " + noteId); + //IllegalArgumentException是非法传参异常,也就是参数传的类型冲突,属于RunTimeException运行时异常 + } + + public static String getFormattedSnippet(String snippet) {//对字符串进行格式处理,将字符串两头的空格去掉,同时将换行符去掉 + if (snippet != null) { + snippet = snippet.trim();//trim()函数,将字符串两头的空格去除 + int index = snippet.indexOf('\n'); + if (index != -1) { + snippet = snippet.substring(0, index);//截取到第一个换行符 + } + } + return snippet; + } +} diff --git a/src/notes/tool/GTaskStringUtils.java b/src/notes/tool/GTaskStringUtils.java new file mode 100644 index 0000000..c279fcd --- /dev/null +++ b/src/notes/tool/GTaskStringUtils.java @@ -0,0 +1,114 @@ +/* + * 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. + */ +//定义了很多的静态字符串,目的就是为了提供jsonObject中相应字符串的"key"。 +// 把这些静态的定义单独写到了一个类里面,这是非常好的编程规范 +package net.micode.notes.tool; + +public class GTaskStringUtils { + + public final static String GTASK_JSON_ACTION_ID = "action_id";//行动ID + + public final static String GTASK_JSON_ACTION_LIST = "action_list";//任务列表 + + public final static String GTASK_JSON_ACTION_TYPE = "action_type";//任务类型 + + public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";//新建 + + public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; + + public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";//移动 + + public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";//更新 + + public final static String GTASK_JSON_CREATOR_ID = "creator_id"; + + public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";//子实体 + + public final static String GTASK_JSON_CLIENT_VERSION = "client_version";//客户端 + + public final static String GTASK_JSON_COMPLETED = "completed"; + + public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";//当前列表位置 + + public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; + + public final static String GTASK_JSON_DELETED = "deleted";//删除 + + public final static String GTASK_JSON_DEST_LIST = "dest_list"; + + public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; + + public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; + + public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; + + public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; + + public final static String GTASK_JSON_GET_DELETED = "get_deleted"; + + public final static String GTASK_JSON_ID = "id"; + + public final static String GTASK_JSON_INDEX = "index";//索引 + + public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; + + public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; + + public final static String GTASK_JSON_LIST_ID = "list_id"; + + public final static String GTASK_JSON_LISTS = "lists"; + + public final static String GTASK_JSON_NAME = "name"; + + public final static String GTASK_JSON_NEW_ID = "new_id"; + + public final static String GTASK_JSON_NOTES = "notes"; + + public final static String GTASK_JSON_PARENT_ID = "parent_id"; + + public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; + + public final static String GTASK_JSON_RESULTS = "results"; + + public final static String GTASK_JSON_SOURCE_LIST = "source_list"; + + public final static String GTASK_JSON_TASKS = "tasks";//任务栏 + + public final static String GTASK_JSON_TYPE = "type"; + + public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; + + public final static String GTASK_JSON_TYPE_TASK = "TASK";//任务 + + public final static String GTASK_JSON_USER = "user"; + + public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; + + public final static String FOLDER_DEFAULT = "Default"; + + public final static String FOLDER_CALL_NOTE = "Call_Note";//呼叫小米便签 + + public final static String FOLDER_META = "METADATA"; + + public final static String META_HEAD_GTASK_ID = "meta_gid"; + + public final static String META_HEAD_NOTE = "meta_note"; + + public final static String META_HEAD_DATA = "meta_data";//数据 + + public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; + +} diff --git a/src/notes/tool/ResourceParser.java b/src/notes/tool/ResourceParser.java new file mode 100644 index 0000000..edf98d9 --- /dev/null +++ b/src/notes/tool/ResourceParser.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//这个类定义了一个对象的不同属性 +package net.micode.notes.tool; +//引用便签所需要的各个库 +import android.content.Context; +import android.preference.PreferenceManager; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesPreferenceActivity; +//ResourceParser类获取程序资源,如图片颜色等 +public class ResourceParser {//ResourceParser类获取程序资源,如图片颜色等 +//为颜色静态常量赋值 + public static final int YELLOW = 0; + public static final int BLUE = 1; + public static final int WHITE = 2; + public static final int GREEN = 3; + public static final int RED = 4; + + public static final int BG_DEFAULT_COLOR = YELLOW;// 默认背景颜色是黄色 + + public static final int TEXT_SMALL = 0;//对字体大小的静态常量赋值 + public static final int TEXT_MEDIUM = 1; + public static final int TEXT_LARGE = 2; + public static final int TEXT_SUPER = 3; + + public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;//默认字体大小是中等大小 + + public static class NoteBgResources {//Note的背景颜色 + private final static int [] BG_EDIT_RESOURCES = new int [] {//调用drawable中的五种颜色的背景图片png文件 + R.drawable.edit_yellow, + R.drawable.edit_blue, + R.drawable.edit_white, + R.drawable.edit_green, + R.drawable.edit_red + }; + + private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {//背景标题的资源常量数组 + R.drawable.edit_title_yellow, + R.drawable.edit_title_blue, + R.drawable.edit_title_white, + R.drawable.edit_title_green, + R.drawable.edit_title_red + }; + + public static int getNoteBgResource(int id) { + return BG_EDIT_RESOURCES[id]; + }//数组调用便签背景图片资源文件 + + public static int getNoteTitleBgResource(int id) { + return BG_EDIT_TITLE_RESOURCES[id]; + }//数组调用标题背景图片资源文件 + } + + public static int getDefaultBgId(Context context) {//直接获取默认的背景颜色 + if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(//如果颜色设定为随机的颜色,那么就随机返回一个颜色 + NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { + return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); + } else { + return BG_DEFAULT_COLOR; + } + } + + public static class NoteItemBgResources {//便签背景资源类 + private final static int [] BG_FIRST_RESOURCES = new int [] {//不同drawable的变量声明 + R.drawable.list_yellow_up, + R.drawable.list_blue_up, + R.drawable.list_white_up, + R.drawable.list_green_up, + R.drawable.list_red_up + }; + + private final static int [] BG_NORMAL_RESOURCES = new int [] {//定义了背景的默认资源 + R.drawable.list_yellow_middle, + R.drawable.list_blue_middle, + R.drawable.list_white_middle, + R.drawable.list_green_middle, + R.drawable.list_red_middle + }; + private final static int [] BG_LAST_RESOURCES = new int [] {//定义背景的下方资源 + R.drawable.list_yellow_down, + R.drawable.list_blue_down, + R.drawable.list_white_down, + R.drawable.list_green_down, + R.drawable.list_red_down, + }; + + private final static int [] BG_SINGLE_RESOURCES = new int [] { + R.drawable.list_yellow_single, + R.drawable.list_blue_single, + R.drawable.list_white_single, + R.drawable.list_green_single, + R.drawable.list_red_single + }; +// 根据id返回四种类型便签项目背景资源的方法 + public static int getNoteBgFirstRes(int id) { + return BG_FIRST_RESOURCES[id]; + }//通过ID寻找first的颜色值 + + public static int getNoteBgLastRes(int id) { + + return BG_LAST_RESOURCES[id]; + }//通过ID寻找last的颜色值 + + public static int getNoteBgSingleRes(int id) { + return BG_SINGLE_RESOURCES[id]; + }//通过ID获取单个便签背景颜色资源 + + public static int getNoteBgNormalRes(int id) { + return BG_NORMAL_RESOURCES[id]; + }//通过ID寻找normal的颜色值 + + public static int getFolderBgRes() { + return R.drawable.list_folder; + }//对widget的内置颜色变量等的声明 + } + + public static class WidgetBgResources {//小窗口情况下的背景资源类 + private final static int [] BG_2X_RESOURCES = new int [] {//两倍大小的窗口的颜色子类的值 + R.drawable.widget_2x_yellow, + R.drawable.widget_2x_blue, + R.drawable.widget_2x_white, + R.drawable.widget_2x_green, + R.drawable.widget_2x_red, + }; + + public static int getWidget2xBgResource(int id) { + return BG_2X_RESOURCES[id]; + }//根据ID加载BG_2X_RESOURCES数组里的颜色资源序号 + + private final static int [] BG_4X_RESOURCES = new int [] {//4x小窗口资源初始化 + R.drawable.widget_4x_yellow, + R.drawable.widget_4x_blue, + R.drawable.widget_4x_white, + R.drawable.widget_4x_green, + R.drawable.widget_4x_red + }; + + public static int getWidget4xBgResource(int id) { + return BG_4X_RESOURCES[id]; + }//根据ID加载BG_4X_RESOURCES数组里的颜色资源序号 + } + + public static class TextAppearanceResources {//文本外观资源,包括默认字体,以及获取资源大小 + private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {//文本字体的四种大小 + R.style.TextAppearanceNormal, + R.style.TextAppearanceMedium, + R.style.TextAppearanceLarge, + R.style.TextAppearanceSuper + }; + + public static int getTexAppearanceResource(int id) {//检测ID是否大于字体大小资源总量,如果是返回默认的结果,如果不是,则返回大小的ID号 + /** + * HACKME: Fix bug of store the resource id in shared preference. + * The id may larger than the length of resources, in this case, + * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} + */ + if (id >= TEXTAPPEARANCE_RESOURCES.length) {//若输入id大于字体编号最大值,则返回默认值 + return BG_DEFAULT_FONT_SIZE; + } + return TEXTAPPEARANCE_RESOURCES[id]; + } + + public static int getResourcesSize() { + return TEXTAPPEARANCE_RESOURCES.length; + }//返回字体大小资源的长度 + } +}