You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.model;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.RemoteException;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
/**
* 表示一个便签的模型类,负责便签的创建、本地修改管理和数据同步
*/
public class Note {
// 存储便签元数据(如标题、父文件夹等)的变更值
private ContentValues mNoteDiffValues;
// 存储便签内容数据(文本/通话记录)
private NoteData mNoteData;
private static final String TAG = "Note";
/**
* 在指定文件夹中创建新便签并返回其ID
* @param context 上下文对象
* @param folderId 父文件夹ID
* @return 新创建的便签ID大于0
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// 初始化便签基础属性
ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis();
values.put(NoteColumns.CREATED_DATE, createdTime);
values.put(NoteColumns.MODIFIED_DATE, createdTime);
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
values.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记为本地已修改
values.put(NoteColumns.PARENT_ID, folderId);
// 插入数据库并获取URI
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
// 从URI解析便签ID
long noteId = 0;
try {
noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0;
}
if (noteId == -1) {
throw new IllegalStateException("Wrong note id:" + noteId);
}
return noteId;
}
/** 构造函数:初始化便签数据和变更容器 */
public Note() {
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
/**
* 设置便签元数据如标题、父ID等
* @param key 字段名NoteColumns中的常量
* @param value 字段值
*/
public void setNoteValue(String key, String value) {
mNoteDiffValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记本地修改
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); // 更新修改时间
}
/** 设置文本类型便签的内容数据 */
public void setTextData(String key, String value) {
mNoteData.setTextData(key, value);
}
/** 设置文本数据项的ID用于更新现有数据 */
public void setTextDataId(long id) {
mNoteData.setTextDataId(id);
}
/** 获取文本数据项的ID */
public long getTextDataId() {
return mNoteData.mTextDataId;
}
/** 设置通话记录数据项的ID用于更新现有数据 */
public void setCallDataId(long id) {
mNoteData.setCallDataId(id);
}
/** 设置通话记录类型便签的内容数据 */
public void setCallData(String key, String value) {
mNoteData.setCallData(key, value);
}
/** 检查便签是否有未同步的本地修改 */
public boolean isLocalModified() {
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
/**
* 同步便签数据到数据库
* @param context 上下文对象
* @param noteId 要同步的便签ID
* @return true 同步成功false 同步失败
*/
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
if (!isLocalModified()) {
return true; // 无修改直接返回成功
}
// 步骤1: 更新便签元数据
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
mNoteDiffValues, null, null) == 0) {
Log.e(TAG, "Update note error, should not happen");
// 继续尝试保存内容数据(即使元数据更新失败)
}
mNoteDiffValues.clear(); // 清空缓存
// 步骤2: 更新便签内容数据(文本/通话记录)
if (mNoteData.isLocalModified() &&
(mNoteData.pushIntoContentResolver(context, noteId) == null)) {
return false; // 内容数据同步失败
}
return true;
}
/**
* 内部类:管理便签的内容数据(文本/通话记录)
*/
private class NoteData {
private long mTextDataId; // 文本数据项在数据库中的ID
private ContentValues mTextDataValues; // 待同步的文本数据变更
private long mCallDataId; // 通话记录数据项在数据库中的ID
private ContentValues mCallDataValues; // 待同步的通话记录变更
private static final String TAG = "NoteData";
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0; // 0表示新数据未插入数据库
mCallDataId = 0;
}
/** 检查内容数据是否有修改 */
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
void setTextDataId(long id) {
if(id <= 0) {
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
}
void setCallDataId(long id) {
if (id <= 0) {
throw new IllegalArgumentException("Call data id should larger than 0");
}
mCallDataId = id;
}
/** 设置通话记录数据并标记便签为已修改 */
void setCallData(String key, String value) {
mCallDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/** 设置文本数据并标记便签为已修改 */
void setTextData(String key, String value) {
mTextDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
* 将内容数据变更推送到数据库
* @return Uri 成功时返回便签URI失败返回null
*/
Uri pushIntoContentResolver(Context context, long noteId) {
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
ContentProviderOperation.Builder builder = null;
// 处理文本数据变更
if(mTextDataValues.size() > 0) {
mTextDataValues.put(DataColumns.NOTE_ID, noteId); // 关联便签ID
if (mTextDataId == 0) {
// 新数据:插入操作
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mTextDataValues);
try {
setTextDataId(ContentUris.parseId(uri)); // 记录新数据的ID
} catch (Exception e) {
Log.e(TAG, "Insert new text data fail with noteId" + noteId);
mTextDataValues.clear();
return null;
}
} else {
// 现有数据:更新操作
builder = ContentProviderOperation.newUpdate(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, mTextDataId));
builder.withValues(mTextDataValues);
operationList.add(builder.build());
}
mTextDataValues.clear();
}
// 处理通话记录变更(逻辑同上)
if(mCallDataValues.size() > 0) {
mCallDataValues.put(DataColumns.NOTE_ID, noteId);
if (mCallDataId == 0) {
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mCallDataValues);
try {
setCallDataId(ContentUris.parseId(uri));
} catch (Exception e) {
Log.e(TAG, "Insert new call data fail with noteId" + noteId);
mCallDataValues.clear();
return null;
}
} else {
builder = ContentProviderOperation.newUpdate(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, mCallDataId));
builder.withValues(mCallDataValues);
operationList.add(builder.build());
}
mCallDataValues.clear();
}
// 批量执行更新操作(如果有)
if (!operationList.isEmpty()) {
try {
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
// 返回便签URI表示成功
return (results == null || results.length == 0) ? null :
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Batch operation error: " + e.getMessage());
return null;
}
}
return null; // 无内容数据更新
}
}
}