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