/* * 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 * @throws IllegalStateException 当创建便签失败时抛出 */ public static synchronized long getNewNoteId(Context context, long folderId) { // Create a new note in the database 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 = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); 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(); // 初始化便签数据 } /** * 设置便签的属性值 * @param key 属性名 * @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()); // 更新修改时间 } /** * 设置文本便签的数据 * @param key 数据键 * @param value 数据值 */ public void setTextData(String key, String value) { mNoteData.setTextData(key, value); } /** * 设置文本便签的数据ID * @param id 文本便签数据ID */ public void setTextDataId(long id) { mNoteData.setTextDataId(id); } /** * 获取文本便签的数据ID * @return 文本便签数据ID */ public long getTextDataId() { return mNoteData.mTextDataId; } /** * 设置通话记录便签的数据ID * @param id 通话记录便签数据ID */ public void setCallDataId(long id) { mNoteData.setCallDataId(id); } /** * 设置通话记录便签的数据 * @param key 数据键 * @param value 数据值 */ public void setCallData(String key, String value) { mNoteData.setCallData(key, value); } /** * 检查便签是否在本地被修改 * @return 如果便签在本地被修改则返回true,否则返回false */ public boolean isLocalModified() { return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); } /** * 将便签的修改同步到数据库 * @param context 上下文对象 * @param noteId 便签ID * @return 同步是否成功 * @throws IllegalArgumentException 当便签ID无效时抛出 */ public boolean syncNote(Context context, long noteId) { if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); } // 如果便签没有被修改,直接返回成功 if (!isLocalModified()) { return true; } /** * 理论上,一旦数据改变,便签应该更新 {@link NoteColumns#LOCAL_MODIFIED} 和 * {@link NoteColumns#MODIFIED_DATE}。为了数据安全,即使更新便签失败,我们也会更新 * 便签数据信息 */ // 更新便签属性 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(); // 清空便签差异值 // 如果便签数据被修改,将数据同步到数据库 if (mNoteData.isLocalModified() && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { return false; // 数据同步失败,返回false } return true; // 同步成功 } /** * 便签数据内部类,用于管理便签的具体内容 * 支持普通文本便签和通话记录便签 */ private class NoteData { /** * 文本便签的数据ID */ private long mTextDataId; /** * 文本便签的差异值,用于存储文本便签的修改内容 */ private ContentValues mTextDataValues; /** * 通话记录便签的数据ID */ private long mCallDataId; /** * 通话记录便签的差异值,用于存储通话记录便签的修改内容 */ private ContentValues mCallDataValues; /** * 日志标签,用于在日志系统中标识本类的日志信息 */ private static final String TAG = "NoteData"; /** * 构造方法,初始化便签数据对象 */ public NoteData() { mTextDataValues = new ContentValues(); // 初始化文本便签差异值 mCallDataValues = new ContentValues(); // 初始化通话记录便签差异值 mTextDataId = 0; // 初始化文本便签数据ID mCallDataId = 0; // 初始化通话记录便签数据ID } /** * 检查便签数据是否在本地被修改 * @return 如果便签数据在本地被修改则返回true,否则返回false */ boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } /** * 设置文本便签的数据ID * @param id 文本便签数据ID * @throws IllegalArgumentException 当ID小于等于0时抛出 */ void setTextDataId(long id) { if(id <= 0) { throw new IllegalArgumentException("Text data id should larger than 0"); } mTextDataId = id; } /** * 设置通话记录便签的数据ID * @param id 通话记录便签数据ID * @throws IllegalArgumentException 当ID小于等于0时抛出 */ void setCallDataId(long id) { if (id <= 0) { throw new IllegalArgumentException("Call data id should larger than 0"); } mCallDataId = id; } /** * 设置通话记录便签的数据 * @param key 数据键 * @param value 数据值 */ void setCallData(String key, String value) { mCallDataValues.put(key, value); // 存储通话记录数据变化 mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记为本地修改 mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); // 更新修改时间 } /** * 设置文本便签的数据 * @param key 数据键 * @param value 数据值 */ void setTextData(String key, String value) { mTextDataValues.put(key, value); // 存储文本数据变化 mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记为本地修改 mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); // 更新修改时间 } /** * 将便签数据推送到ContentResolver * @param context 上下文对象 * @param noteId 便签ID * @return 操作成功返回null,失败返回null * @throws IllegalArgumentException 当便签ID无效时抛出 */ Uri pushIntoContentResolver(Context context, long noteId) { /** * 安全性检查 */ if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); } // 创建ContentProvider操作列表,用于批量处理数据操作 ArrayList 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); // 设置MIME类型 Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mTextDataValues); try { setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); // 获取新插入的数据ID } catch (NumberFormatException e) { Log.e(TAG, "Insert new text data fail with noteId" + noteId); mTextDataValues.clear(); return null; // 插入失败,返回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); // 设置便签ID if (mCallDataId == 0) { // 插入新的通话记录便签数据 mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); // 设置MIME类型 Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mCallDataValues); try { setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); // 获取新插入的数据ID } catch (NumberFormatException e) { Log.e(TAG, "Insert new call data fail with noteId" + noteId); mCallDataValues.clear(); return null; // 插入失败,返回null } } else { // 更新现有通话记录便签数据 builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mCallDataId)); builder.withValues(mCallDataValues); operationList.add(builder.build()); } mCallDataValues.clear(); // 清空通话记录便签差异值 } // 如果有操作需要执行,应用批量操作 if (operationList.size() > 0) { try { ContentProviderResult[] results = context.getContentResolver().applyBatch( Notes.AUTHORITY, operationList); // 返回操作结果,成功返回便签URI,失败返回null return (results == null || results.length == 0 || results[0] == null) ? null : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); return null; // 远程异常,返回null } catch (OperationApplicationException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); return null; // 操作应用异常,返回null } } return null; // 没有操作需要执行,返回null } } }