Compare commits
No commits in common. 'master' and 'develop' have entirely different histories.
Binary file not shown.
Binary file not shown.
@ -1,102 +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.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;// 需要在远程云端增加内容
|
||||
|
||||
public static final int SYNC_ACTION_ADD_LOCAL = 2;// 需要在本地增加内容
|
||||
|
||||
public static final int SYNC_ACTION_DEL_REMOTE = 3;// 需要在本地增加内容
|
||||
|
||||
public static final int SYNC_ACTION_DEL_LOCAL = 4;// 需要在远程云端删除内容
|
||||
|
||||
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;// 需要在本地删除内容
|
||||
|
||||
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;// 需要将本地内容更新到远程云端
|
||||
|
||||
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;// 需要将远程云端内容更新到本地
|
||||
|
||||
public static final int SYNC_ACTION_ERROR = 8;// 同步出现错误
|
||||
|
||||
private String mGid;
|
||||
|
||||
private String mName;
|
||||
|
||||
private long mLastModified;//记录最后一次修改时间
|
||||
|
||||
private boolean mDeleted;//记录最后一次修改时间
|
||||
|
||||
public Node() {
|
||||
mGid = null;
|
||||
mName = "";
|
||||
mLastModified = 0;
|
||||
mDeleted = false;
|
||||
}
|
||||
|
||||
public abstract JSONObject getCreateAction(int actionId);
|
||||
|
||||
public abstract JSONObject getUpdateAction(int actionId);
|
||||
|
||||
public abstract void setContentByRemoteJSON(JSONObject js);
|
||||
|
||||
public abstract void setContentByLocalJSON(JSONObject js);
|
||||
|
||||
public abstract JSONObject getLocalJSONFromContent();
|
||||
|
||||
public abstract int getSyncAction(Cursor c);
|
||||
|
||||
public void setGid(String gid) {
|
||||
this.mGid = gid;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.mName = name;
|
||||
}
|
||||
|
||||
public void setLastModified(long lastModified) {
|
||||
this.mLastModified = lastModified;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.mDeleted = deleted;
|
||||
}
|
||||
|
||||
public String getGid() {
|
||||
return this.mGid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.mName;
|
||||
}
|
||||
|
||||
public long getLastModified() {
|
||||
return this.mLastModified;
|
||||
}
|
||||
|
||||
public boolean getDeleted() {
|
||||
return this.mDeleted;
|
||||
}
|
||||
|
||||
}
|
@ -1,351 +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.gtask.data;
|
||||
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
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 net.micode.notes.gtask.exception.ActionFailureException;
|
||||
import net.micode.notes.tool.GTaskStringUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class Task extends Node {
|
||||
private static final String TAG = Task.class.getSimpleName();
|
||||
|
||||
private boolean mCompleted;
|
||||
|
||||
private String mNotes;
|
||||
|
||||
private JSONObject mMetaInfo;
|
||||
|
||||
private Task mPriorSibling;
|
||||
|
||||
private TaskList mParent;
|
||||
|
||||
public Task() {
|
||||
super();
|
||||
mCompleted = false;
|
||||
mNotes = null;
|
||||
mPriorSibling = null;
|
||||
mParent = null;
|
||||
mMetaInfo = null;
|
||||
}
|
||||
|
||||
|
||||
public JSONObject getCreateAction(int actionId) {
|
||||
JSONObject js = new JSONObject();
|
||||
|
||||
try {
|
||||
// action_type
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
|
||||
|
||||
// action_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
|
||||
|
||||
// index
|
||||
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));
|
||||
|
||||
// entity_delta
|
||||
JSONObject entity = new JSONObject();
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_TYPE_TASK);
|
||||
if (getNotes() != null) {
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
|
||||
}
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
|
||||
|
||||
// parent_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid());
|
||||
|
||||
// dest_parent_type
|
||||
js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_TYPE_GROUP);
|
||||
|
||||
// list_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid());
|
||||
|
||||
// prior_sibling_id
|
||||
if (mPriorSibling != null) {
|
||||
js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid());
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
throw new ActionFailureException("fail to generate task-create jsonobject");
|
||||
}
|
||||
|
||||
return js;
|
||||
}
|
||||
|
||||
public JSONObject getUpdateAction(int actionId) {
|
||||
JSONObject js = new JSONObject();
|
||||
try {
|
||||
// action_type
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
|
||||
|
||||
// action_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
|
||||
|
||||
// id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());
|
||||
|
||||
// entity_delta
|
||||
JSONObject entity = new JSONObject();
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
|
||||
if (getNotes() != null) {
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
|
||||
}
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
throw new ActionFailureException("fail to generate task-update jsonobject");
|
||||
}
|
||||
|
||||
return js;
|
||||
}
|
||||
|
||||
public void setContentByRemoteJSON(JSONObject js) {
|
||||
if (js != null) {
|
||||
try {
|
||||
// id
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {
|
||||
setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
|
||||
}
|
||||
|
||||
// last_modified
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
|
||||
setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
|
||||
}
|
||||
|
||||
// name
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
|
||||
setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
|
||||
}
|
||||
|
||||
// notes
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) {
|
||||
setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES));
|
||||
}
|
||||
|
||||
// deleted
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) {
|
||||
setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED));
|
||||
}
|
||||
|
||||
// completed
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) {
|
||||
setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
throw new ActionFailureException("fail to get task content from jsonobject");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setContentByLocalJSON(JSONObject js) {
|
||||
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)
|
||||
|| !js.has(GTaskStringUtils.META_HEAD_DATA)) {
|
||||
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
|
||||
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
|
||||
|
||||
if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) {
|
||||
Log.e(TAG, "invalid type");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < dataArray.length(); i++) {
|
||||
JSONObject data = dataArray.getJSONObject(i);
|
||||
if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
|
||||
setName(data.getString(DataColumns.CONTENT));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject getLocalJSONFromContent() {
|
||||
String name = getName();
|
||||
try {
|
||||
if (mMetaInfo == null) {
|
||||
// new task created from web
|
||||
if (name == null) {
|
||||
Log.w(TAG, "the note seems to be an empty one");
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject js = new JSONObject();
|
||||
JSONObject note = new JSONObject();
|
||||
JSONArray dataArray = new JSONArray();
|
||||
JSONObject data = new JSONObject();
|
||||
data.put(DataColumns.CONTENT, name);
|
||||
dataArray.put(data);
|
||||
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
|
||||
note.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
|
||||
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
|
||||
return js;
|
||||
} else {
|
||||
// synced task
|
||||
JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
|
||||
JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
|
||||
|
||||
for (int i = 0; i < dataArray.length(); i++) {
|
||||
JSONObject data = dataArray.getJSONObject(i);
|
||||
if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
|
||||
data.put(DataColumns.CONTENT, getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
note.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
|
||||
return mMetaInfo;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMetaInfo(MetaData metaData) {
|
||||
if (metaData != null && metaData.getNotes() != null) {
|
||||
try {
|
||||
mMetaInfo = new JSONObject(metaData.getNotes());
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, e.toString());
|
||||
mMetaInfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getSyncAction(Cursor c) {
|
||||
try {
|
||||
JSONObject noteInfo = null;
|
||||
if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) {
|
||||
noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
|
||||
}
|
||||
|
||||
if (noteInfo == null) {
|
||||
Log.w(TAG, "it seems that note meta has been deleted");
|
||||
return SYNC_ACTION_UPDATE_REMOTE;
|
||||
}
|
||||
|
||||
if (!noteInfo.has(NoteColumns.ID)) {
|
||||
Log.w(TAG, "remote note id seems to be deleted");
|
||||
return SYNC_ACTION_UPDATE_LOCAL;
|
||||
}
|
||||
|
||||
// validate the note id now
|
||||
if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) {
|
||||
Log.w(TAG, "note id doesn't match");
|
||||
return SYNC_ACTION_UPDATE_LOCAL;
|
||||
}
|
||||
|
||||
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
|
||||
// there is no local update
|
||||
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
|
||||
// no update both side
|
||||
return SYNC_ACTION_NONE;
|
||||
} else {
|
||||
// apply remote to local
|
||||
return SYNC_ACTION_UPDATE_LOCAL;
|
||||
}
|
||||
} else {
|
||||
// validate gtask id
|
||||
if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {
|
||||
Log.e(TAG, "gtask id doesn't match");
|
||||
return SYNC_ACTION_ERROR;
|
||||
}
|
||||
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
|
||||
// local modification only
|
||||
return SYNC_ACTION_UPDATE_REMOTE;
|
||||
} else {
|
||||
return SYNC_ACTION_UPDATE_CONFLICT;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return SYNC_ACTION_ERROR;
|
||||
}
|
||||
|
||||
public boolean isWorthSaving() {
|
||||
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
|
||||
|| (getNotes() != null && getNotes().trim().length() > 0);
|
||||
}
|
||||
|
||||
public void setCompleted(boolean completed) {
|
||||
this.mCompleted = completed;
|
||||
}
|
||||
|
||||
public void setNotes(String notes) {
|
||||
this.mNotes = notes;
|
||||
}
|
||||
|
||||
public void setPriorSibling(Task priorSibling) {
|
||||
this.mPriorSibling = priorSibling;
|
||||
}
|
||||
|
||||
public void setParent(TaskList parent) {
|
||||
this.mParent = parent;
|
||||
}
|
||||
|
||||
public boolean getCompleted() {
|
||||
return this.mCompleted;
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return this.mNotes;
|
||||
}
|
||||
|
||||
public Task getPriorSibling() {
|
||||
return this.mPriorSibling;
|
||||
}
|
||||
|
||||
public TaskList getParent() {
|
||||
return this.mParent;
|
||||
}
|
||||
|
||||
}
|
@ -1,318 +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.tool;
|
||||
|
||||
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"; // 定义日志标签
|
||||
private static BackupUtils sInstance; // 单例模式实例
|
||||
|
||||
// 获取单例实例的方法,确保整个应用中只有一个 BackupUtils 对象
|
||||
public static synchronized BackupUtils getInstance(Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new BackupUtils(context);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
// 定义备份和恢复状态的常量
|
||||
public static final int STATE_SD_CARD_UNMOUONTED = 0; // SD 卡未挂载
|
||||
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; // 备份文件不存在
|
||||
public static final int STATE_DATA_DESTROIED = 2; // 数据损坏
|
||||
public static final int STATE_SYSTEM_ERROR = 3; // 系统错误
|
||||
public static final int STATE_SUCCESS = 4; // 备份或恢复成功
|
||||
|
||||
private TextExport mTextExport; // 文本导出工具
|
||||
|
||||
private BackupUtils(Context context) {
|
||||
mTextExport = new TextExport(context); // 初始化 TextExport 对象
|
||||
}
|
||||
|
||||
// 检查外部存储是否可用
|
||||
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 {
|
||||
// 定义查询数据所需的列
|
||||
private static final String[] NOTE_PROJECTION = {
|
||||
NoteColumns.ID, // 笔记ID
|
||||
NoteColumns.MODIFIED_DATE, // 修改时间
|
||||
NoteColumns.SNIPPET, // 笔记的摘要
|
||||
NoteColumns.TYPE // 笔记类型
|
||||
};
|
||||
|
||||
private static final int NOTE_COLUMN_ID = 0;
|
||||
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, // MIME 类型
|
||||
DataColumns.DATA1, // 备用数据1
|
||||
DataColumns.DATA2, // 备用数据2
|
||||
DataColumns.DATA3, // 备用数据3
|
||||
DataColumns.DATA4, // 备用数据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;
|
||||
|
||||
// 定义文本格式化的常量
|
||||
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) {
|
||||
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
|
||||
mContext = context;
|
||||
mFileName = "";
|
||||
mFileDirectory = "";
|
||||
}
|
||||
|
||||
// 获取格式化字符串
|
||||
private String getFormat(int id) {
|
||||
return TEXT_FORMAT[id];
|
||||
}
|
||||
|
||||
// 导出文件夹下的所有笔记
|
||||
private void exportFolderToText(String folderId, PrintStream ps) {
|
||||
// 查询该文件夹下的所有笔记
|
||||
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
|
||||
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[]{folderId}, null);
|
||||
|
||||
if (notesCursor != null) {
|
||||
if (notesCursor.moveToFirst()) {
|
||||
do {
|
||||
// 打印笔记的最后修改时间
|
||||
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
|
||||
mContext.getString(R.string.format_datetime_mdhm),
|
||||
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
|
||||
// 获取笔记的 ID
|
||||
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
|
||||
exportNoteToText(noteId, ps); // 导出笔记的内容
|
||||
} while (notesCursor.moveToNext());
|
||||
}
|
||||
notesCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 导出指定 ID 的笔记内容
|
||||
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) {
|
||||
if (dataCursor.moveToFirst()) {
|
||||
do {
|
||||
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
|
||||
if (DataConstants.CALL_NOTE.equals(mimeType)) {
|
||||
// 如果是电话记录类型,导出电话信息
|
||||
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));
|
||||
}
|
||||
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat.format(
|
||||
mContext.getString(R.string.format_datetime_mdhm), callDate)));
|
||||
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();
|
||||
}
|
||||
|
||||
// 在笔记之间添加分隔行
|
||||
try {
|
||||
ps.write(new byte[]{Character.LINE_SEPARATOR, Character.LETTER_NUMBER});
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// 导出为用户可读的文本格式
|
||||
public int exportToText() {
|
||||
if (!externalStorageAvailable()) {
|
||||
Log.d(TAG, "Media was not mounted");
|
||||
return STATE_SD_CARD_UNMOUONTED; // SD 卡未挂载
|
||||
}
|
||||
|
||||
PrintStream ps = getExportToTextPrintStream();
|
||||
if (ps == null) {
|
||||
Log.e(TAG, "get print stream error");
|
||||
return STATE_SYSTEM_ERROR; // 获取输出流失败
|
||||
}
|
||||
|
||||
// 导出文件夹和文件夹下的所有笔记
|
||||
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 {
|
||||
// 打印文件夹名称
|
||||
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);
|
||||
exportFolderToText(folderId, ps); // 导出该文件夹下的笔记
|
||||
} while (folderCursor.moveToNext());
|
||||
}
|
||||
folderCursor.close();
|
||||
}
|
||||
|
||||
// 导出根文件夹下的所有笔记
|
||||
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))));
|
||||
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
|
||||
exportNoteToText(noteId, ps); // 导出该笔记内容
|
||||
} while (noteCursor.moveToNext());
|
||||
}
|
||||
noteCursor.close();
|
||||
}
|
||||
|
||||
ps.close(); // 关闭输出流
|
||||
return STATE_SUCCESS; // 导出成功
|
||||
}
|
||||
|
||||
// 获取指向导出文本文件的输出流
|
||||
private PrintStream getExportToTextPrintStream() {
|
||||
File file = generateFileMountedOnSDcard(mContext, R.string.file_path, 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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成保存导入数据的文件
|
||||
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(Environment.getExternalStorageDirectory());
|
||||
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; // 创建失败
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,149 +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.tool;
|
||||
|
||||
public class GTaskStringUtils {
|
||||
|
||||
// GTASK 相关的 JSON 字段名常量
|
||||
|
||||
// action_id - 操作的唯一标识符
|
||||
public final static String GTASK_JSON_ACTION_ID = "action_id";
|
||||
|
||||
// action_list - 用于存储多个操作的列表
|
||||
public final static String GTASK_JSON_ACTION_LIST = "action_list";
|
||||
|
||||
// action_type - 操作的类型
|
||||
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"; // 更新任务
|
||||
|
||||
// creator_id - 创建者的 ID
|
||||
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
|
||||
|
||||
// child_entity - 子任务实体
|
||||
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
|
||||
|
||||
// client_version - 客户端版本
|
||||
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
|
||||
|
||||
// completed - 任务是否完成
|
||||
public final static String GTASK_JSON_COMPLETED = "completed";
|
||||
|
||||
// current_list_id - 当前任务所属的列表 ID
|
||||
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
|
||||
|
||||
// default_list_id - 默认任务列表 ID
|
||||
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
|
||||
|
||||
// deleted - 任务是否被删除
|
||||
public final static String GTASK_JSON_DELETED = "deleted";
|
||||
|
||||
// dest_list - 目标任务列表
|
||||
public final static String GTASK_JSON_DEST_LIST = "dest_list";
|
||||
|
||||
// dest_parent - 目标任务父级 ID
|
||||
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
|
||||
|
||||
// dest_parent_type - 目标任务父级类型
|
||||
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
|
||||
|
||||
// entity_delta - 实体增量数据
|
||||
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
|
||||
|
||||
// entity_type - 实体类型(如任务或文件夹)
|
||||
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
|
||||
|
||||
// get_deleted - 获取已删除的任务
|
||||
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
|
||||
|
||||
// id - 任务或实体的 ID
|
||||
public final static String GTASK_JSON_ID = "id";
|
||||
|
||||
// index - 任务在列表中的索引位置
|
||||
public final static String GTASK_JSON_INDEX = "index";
|
||||
|
||||
// last_modified - 上次修改时间
|
||||
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
|
||||
|
||||
// latest_sync_point - 最新同步点
|
||||
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
|
||||
|
||||
// list_id - 任务所属列表的 ID
|
||||
public final static String GTASK_JSON_LIST_ID = "list_id";
|
||||
|
||||
// lists - 任务列表集合
|
||||
public final static String GTASK_JSON_LISTS = "lists";
|
||||
|
||||
// name - 任务的名称
|
||||
public final static String GTASK_JSON_NAME = "name";
|
||||
|
||||
// new_id - 新生成的 ID
|
||||
public final static String GTASK_JSON_NEW_ID = "new_id";
|
||||
|
||||
// notes - 任务的备注
|
||||
public final static String GTASK_JSON_NOTES = "notes";
|
||||
|
||||
// parent_id - 任务父级的 ID
|
||||
public final static String GTASK_JSON_PARENT_ID = "parent_id";
|
||||
|
||||
// prior_sibling_id - 任务的前一个兄弟任务 ID
|
||||
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
|
||||
|
||||
// results - 查询结果
|
||||
public final static String GTASK_JSON_RESULTS = "results";
|
||||
|
||||
// source_list - 源任务列表
|
||||
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
|
||||
|
||||
// tasks - 任务集合
|
||||
public final static String GTASK_JSON_TASKS = "tasks";
|
||||
|
||||
// type - 任务类型
|
||||
public final static String GTASK_JSON_TYPE = "type";
|
||||
|
||||
// type 常量
|
||||
public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; // 任务组类型
|
||||
public final static String GTASK_JSON_TYPE_TASK = "TASK"; // 任务类型
|
||||
|
||||
// user - 用户信息
|
||||
public final static String GTASK_JSON_USER = "user";
|
||||
|
||||
// MIUI 文件夹前缀,用于 MIUI 系统
|
||||
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"; // GTASK ID
|
||||
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";
|
||||
|
||||
}
|
@ -1,176 +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.ui;
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.DialogInterface.OnDismissListener;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.tool.DataUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
|
||||
private long mNoteId; // 当前提醒相关的笔记 ID
|
||||
private String mSnippet; // 笔记的片段内容(用于提醒内容)
|
||||
private static final int SNIPPET_PREW_MAX_LEN = 60; // 片段内容的最大长度
|
||||
MediaPlayer mPlayer; // 媒体播放器,用于播放提醒声音
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// 设置没有标题
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
// 获取当前窗口对象
|
||||
final Window win = getWindow();
|
||||
|
||||
// 设置窗口标志,让窗口在锁屏时也能显示
|
||||
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
|
||||
// 如果屏幕是关闭状态,保持屏幕常亮,并打开屏幕
|
||||
if (!isScreenOn()) {
|
||||
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
|
||||
}
|
||||
|
||||
// 获取启动此活动的 Intent
|
||||
Intent intent = getIntent();
|
||||
|
||||
try {
|
||||
// 从 URI 中提取笔记 ID
|
||||
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
|
||||
// 获取笔记片段内容
|
||||
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
|
||||
// 如果片段内容过长,截取并添加提示
|
||||
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
|
||||
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
|
||||
: mSnippet;
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化媒体播放器
|
||||
mPlayer = new MediaPlayer();
|
||||
// 检查该笔记是否有效(存在且可见)
|
||||
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
|
||||
// 显示对话框并播放警报声音
|
||||
showActionDialog();
|
||||
playAlarmSound();
|
||||
} else {
|
||||
finish(); // 如果笔记无效,结束活动
|
||||
}
|
||||
}
|
||||
|
||||
// 检查屏幕是否处于开启状态
|
||||
private boolean isScreenOn() {
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
return pm.isScreenOn();
|
||||
}
|
||||
|
||||
// 播放警报声音
|
||||
private void playAlarmSound() {
|
||||
// 获取默认的警报铃声 URI
|
||||
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
|
||||
|
||||
// 获取当前静音模式下影响的音频流设置
|
||||
int silentModeStreams = Settings.System.getInt(getContentResolver(),
|
||||
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
|
||||
|
||||
// 如果设置了静音影响警报流,则使用此流类型
|
||||
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
|
||||
mPlayer.setAudioStreamType(silentModeStreams);
|
||||
} else {
|
||||
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
|
||||
}
|
||||
|
||||
try {
|
||||
// 设置数据源并准备播放
|
||||
mPlayer.setDataSource(this, url);
|
||||
mPlayer.prepare();
|
||||
mPlayer.setLooping(true); // 设置为循环播放
|
||||
mPlayer.start(); // 开始播放声音
|
||||
} catch (IllegalArgumentException | SecurityException | IllegalStateException | IOException e) {
|
||||
e.printStackTrace(); // 捕获并打印异常
|
||||
}
|
||||
}
|
||||
|
||||
// 显示操作对话框
|
||||
private void showActionDialog() {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
|
||||
dialog.setTitle(R.string.app_name); // 设置对话框标题
|
||||
dialog.setMessage(mSnippet); // 设置对话框内容为笔记片段
|
||||
dialog.setPositiveButton(R.string.notealert_ok, this); // 设置“确定”按钮
|
||||
// 如果屏幕是开启状态,则显示“进入”按钮
|
||||
if (isScreenOn()) {
|
||||
dialog.setNegativeButton(R.string.notealert_enter, this);
|
||||
}
|
||||
// 显示对话框并设置“取消”监听器
|
||||
dialog.show().setOnDismissListener(this);
|
||||
}
|
||||
|
||||
// 按钮点击事件处理
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
// 当点击“进入”按钮时,跳转到笔记编辑页面
|
||||
Intent intent = new Intent(this, NoteEditActivity.class);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.putExtra(Intent.EXTRA_UID, mNoteId); // 传递笔记 ID
|
||||
startActivity(intent); // 启动编辑活动
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 对话框关闭时的处理
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
stopAlarmSound(); // 停止播放警报声音
|
||||
finish(); // 结束当前活动
|
||||
}
|
||||
|
||||
// 停止警报声音
|
||||
private void stopAlarmSound() {
|
||||
if (mPlayer != null) {
|
||||
mPlayer.stop(); // 停止播放
|
||||
mPlayer.release(); // 释放播放器资源
|
||||
mPlayer = null; // 设置播放器为 null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +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.ui;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class AlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// 设置要启动的活动为 AlarmAlertActivity
|
||||
intent.setClass(context, AlarmAlertActivity.class);
|
||||
// 设置启动活动时的标志,使得该活动会启动新的任务栈
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// 启动活动
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
@ -1,402 +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.ui;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.ActionBar;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.gtask.remote.GTaskSyncService;
|
||||
|
||||
public class NotesPreferenceActivity extends PreferenceActivity {
|
||||
// 常量:偏好设置的文件名称和一些设置项的键
|
||||
public static final String PREFERENCE_NAME = "notes_preferences";
|
||||
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
|
||||
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
|
||||
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
|
||||
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
|
||||
private static final String AUTHORITIES_FILTER_KEY = "authorities";
|
||||
|
||||
private PreferenceCategory mAccountCategory; // 账户偏好设置项
|
||||
private GTaskReceiver mReceiver; // 广播接收器,用于监听同步状态
|
||||
private Account[] mOriAccounts; // 原始账户列表
|
||||
private boolean mHasAddedAccount; // 是否已经添加了账户
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
// 设置ActionBar的返回按钮
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
// 加载偏好设置界面
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
|
||||
mReceiver = new GTaskReceiver();
|
||||
|
||||
// 注册广播接收器,监听同步服务的广播
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
|
||||
registerReceiver(mReceiver, filter);
|
||||
|
||||
mOriAccounts = null;
|
||||
|
||||
// 设置列表头部视图
|
||||
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
|
||||
getListView().addHeaderView(header, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// 如果用户添加了新账户,需要自动设置同步账户
|
||||
if (mHasAddedAccount) {
|
||||
Account[] accounts = getGoogleAccounts();
|
||||
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
|
||||
for (Account accountNew : accounts) {
|
||||
boolean found = false;
|
||||
for (Account accountOld : mOriAccounts) {
|
||||
if (TextUtils.equals(accountOld.name, accountNew.name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
setSyncAccount(accountNew.name); // 设置同步账户
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshUI(); // 更新UI界面
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// 注销广播接收器
|
||||
if (mReceiver != null) {
|
||||
unregisterReceiver(mReceiver);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
// 加载账户偏好设置
|
||||
private void loadAccountPreference() {
|
||||
mAccountCategory.removeAll(); // 清空账户设置项
|
||||
|
||||
Preference accountPref = new Preference(this);
|
||||
final String defaultAccount = getSyncAccountName(this);
|
||||
accountPref.setTitle(getString(R.string.preferences_account_title));
|
||||
accountPref.setSummary(getString(R.string.preferences_account_summary));
|
||||
|
||||
// 设置点击事件,处理账户选择
|
||||
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (!GTaskSyncService.isSyncing()) {
|
||||
if (TextUtils.isEmpty(defaultAccount)) {
|
||||
// 如果是第一次设置账户,弹出选择账户对话框
|
||||
showSelectAccountAlertDialog();
|
||||
} else {
|
||||
// 如果已经设置了账户,弹出更改账户确认对话框
|
||||
showChangeAccountConfirmAlertDialog();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(NotesPreferenceActivity.this,
|
||||
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mAccountCategory.addPreference(accountPref); // 添加账户偏好项
|
||||
}
|
||||
|
||||
// 加载同步按钮和最后同步时间
|
||||
private void loadSyncButton() {
|
||||
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
|
||||
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
|
||||
|
||||
// 设置按钮状态
|
||||
if (GTaskSyncService.isSyncing()) {
|
||||
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
|
||||
syncButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
GTaskSyncService.cancelSync(NotesPreferenceActivity.this); // 取消同步
|
||||
}
|
||||
});
|
||||
} else {
|
||||
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
|
||||
syncButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
GTaskSyncService.startSync(NotesPreferenceActivity.this); // 开始同步
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); // 按钮是否可用,取决于是否有同步账户
|
||||
|
||||
// 设置最后同步时间
|
||||
if (GTaskSyncService.isSyncing()) {
|
||||
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
|
||||
lastSyncTimeView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
long lastSyncTime = getLastSyncTime(this);
|
||||
if (lastSyncTime != 0) {
|
||||
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
|
||||
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
|
||||
lastSyncTime)));
|
||||
lastSyncTimeView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
lastSyncTimeView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新UI界面
|
||||
private void refreshUI() {
|
||||
loadAccountPreference(); // 加载账户设置
|
||||
loadSyncButton(); // 加载同步按钮
|
||||
}
|
||||
|
||||
// 显示选择账户的对话框
|
||||
private void showSelectAccountAlertDialog() {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
|
||||
|
||||
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
|
||||
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
|
||||
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
|
||||
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
|
||||
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
|
||||
|
||||
dialogBuilder.setCustomTitle(titleView);
|
||||
dialogBuilder.setPositiveButton(null, null); // 没有默认的"确定"按钮
|
||||
|
||||
Account[] accounts = getGoogleAccounts();
|
||||
String defAccount = getSyncAccountName(this);
|
||||
|
||||
mOriAccounts = accounts;
|
||||
mHasAddedAccount = false;
|
||||
|
||||
// 显示所有Google账户供用户选择
|
||||
if (accounts.length > 0) {
|
||||
CharSequence[] items = new CharSequence[accounts.length];
|
||||
final CharSequence[] itemMapping = items;
|
||||
int checkedItem = -1;
|
||||
int index = 0;
|
||||
for (Account account : accounts) {
|
||||
if (TextUtils.equals(account.name, defAccount)) {
|
||||
checkedItem = index;
|
||||
}
|
||||
items[index++] = account.name;
|
||||
}
|
||||
dialogBuilder.setSingleChoiceItems(items, checkedItem,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setSyncAccount(itemMapping[which].toString()); // 设置选中的账户
|
||||
dialog.dismiss();
|
||||
refreshUI(); // 刷新UI
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
|
||||
dialogBuilder.setView(addAccountView);
|
||||
|
||||
// 添加账户点击事件
|
||||
final AlertDialog dialog = dialogBuilder.show();
|
||||
addAccountView.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
mHasAddedAccount = true;
|
||||
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
|
||||
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
|
||||
"gmail-ls"
|
||||
});
|
||||
startActivityForResult(intent, -1); // 打开账户添加界面
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示更改账户的确认对话框
|
||||
private void showChangeAccountConfirmAlertDialog() {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
|
||||
|
||||
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
|
||||
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
|
||||
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
|
||||
getSyncAccountName(this)));
|
||||
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
|
||||
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
|
||||
dialogBuilder.setCustomTitle(titleView);
|
||||
|
||||
CharSequence[] menuItemArray = new CharSequence[] {
|
||||
getString(R.string.preferences_menu_change_account),
|
||||
getString(R.string.preferences_menu_remove_account),
|
||||
getString(R.string.preferences_menu_cancel)
|
||||
};
|
||||
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == 0) {
|
||||
showSelectAccountAlertDialog(); // 更改账户
|
||||
} else if (which == 1) {
|
||||
removeSyncAccount(); // 移除账户
|
||||
refreshUI(); // 刷新UI
|
||||
}
|
||||
}
|
||||
});
|
||||
dialogBuilder.show();
|
||||
}
|
||||
|
||||
// 获取所有Google账户
|
||||
private Account[] getGoogleAccounts() {
|
||||
AccountManager accountManager = AccountManager.get(this);
|
||||
return accountManager.getAccountsByType("com.google");
|
||||
}
|
||||
|
||||
// 设置同步账户
|
||||
private void setSyncAccount(String account) {
|
||||
if (!getSyncAccountName(this).equals(account)) {
|
||||
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
if (account != null) {
|
||||
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
|
||||
} else {
|
||||
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
|
||||
}
|
||||
editor.commit();
|
||||
|
||||
// 清除最后同步时间
|
||||
setLastSyncTime(this, 0);
|
||||
|
||||
// 清理本地的gtask相关信息
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteColumns.GTASK_ID, "");
|
||||
values.put(NoteColumns.SYNC_ID, 0);
|
||||
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
|
||||
}
|
||||
}).start();
|
||||
|
||||
Toast.makeText(NotesPreferenceActivity.this,
|
||||
getString(R.string.preferences_toast_success_set_accout, account),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
// 移除同步账户
|
||||
private void removeSyncAccount() {
|
||||
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
|
||||
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
|
||||
}
|
||||
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
|
||||
editor.remove(PREFERENCE_LAST_SYNC_TIME);
|
||||
}
|
||||
editor.commit();
|
||||
|
||||
// 清理本地的gtask相关信息
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteColumns.GTASK_ID, "");
|
||||
values.put(NoteColumns.SYNC_ID, 0);
|
||||
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
// 获取同步账户的名称
|
||||
public static String getSyncAccountName(Context context) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
|
||||
}
|
||||
|
||||
// 设置最后同步时间
|
||||
public static void setLastSyncTime(Context context, long time) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
// 获取最后同步时间
|
||||
public static long getLastSyncTime(Context context) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
|
||||
}
|
||||
|
||||
// 广播接收器:用于监听同步状态变化
|
||||
private class GTaskReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
refreshUI();
|
||||
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
|
||||
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
|
||||
syncStatus.setText(intent
|
||||
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 菜单选项处理
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
Intent intent = new Intent(this, NotesListActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue