Compare commits
43 Commits
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 92 KiB |
@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* 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";
|
||||||
|
/**
|
||||||
|
* Create a new note id for adding a new note to databases
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextDataId(long id) {
|
||||||
|
mNoteData.setTextDataId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTextDataId() {
|
||||||
|
return mNoteData.mTextDataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean syncNote(Context context, long noteId) {
|
||||||
|
if (noteId <= 0) {
|
||||||
|
throw new IllegalArgumentException("Wrong note id:" + noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLocalModified()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and
|
||||||
|
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
|
||||||
|
* note data info
|
||||||
|
*/
|
||||||
|
if (context.getContentResolver().update(
|
||||||
|
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
|
||||||
|
null) == 0) {
|
||||||
|
Log.e(TAG, "Update note error, should not happen");
|
||||||
|
// Do not return, fall through
|
||||||
|
}
|
||||||
|
mNoteDiffValues.clear();
|
||||||
|
|
||||||
|
if (mNoteData.isLocalModified()
|
||||||
|
&& (mNoteData.pushIntoContentResolver(context, noteId) == null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NoteData {
|
||||||
|
private long mTextDataId;
|
||||||
|
|
||||||
|
private ContentValues mTextDataValues;
|
||||||
|
|
||||||
|
private long mCallDataId;
|
||||||
|
|
||||||
|
private ContentValues mCallDataValues;
|
||||||
|
|
||||||
|
private static final String TAG = "NoteData";
|
||||||
|
|
||||||
|
public NoteData() {
|
||||||
|
mTextDataValues = new ContentValues();
|
||||||
|
mCallDataValues = new ContentValues();
|
||||||
|
mTextDataId = 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri pushIntoContentResolver(Context context, long noteId) {
|
||||||
|
/**
|
||||||
|
* Check for safety
|
||||||
|
*/
|
||||||
|
if (noteId <= 0) {
|
||||||
|
throw new IllegalArgumentException("Wrong note id:" + noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
|
||||||
|
ContentProviderOperation.Builder builder = null;
|
||||||
|
|
||||||
|
if(mTextDataValues.size() > 0) {
|
||||||
|
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
|
||||||
|
if (mTextDataId == 0) {
|
||||||
|
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
|
||||||
|
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
|
||||||
|
mTextDataValues);
|
||||||
|
try {
|
||||||
|
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
|
||||||
|
} catch (NumberFormatException 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(Long.valueOf(uri.getPathSegments().get(1)));
|
||||||
|
} catch (NumberFormatException 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.size() > 0) {
|
||||||
|
try {
|
||||||
|
ContentProviderResult[] results = context.getContentResolver().applyBatch(
|
||||||
|
Notes.AUTHORITY, operationList);
|
||||||
|
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;
|
||||||
|
} catch (OperationApplicationException e) {
|
||||||
|
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,368 @@
|
|||||||
|
/*
|
||||||
|
* 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.appwidget.AppWidgetManager;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
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.DataConstants;
|
||||||
|
import net.micode.notes.data.Notes.NoteColumns;
|
||||||
|
import net.micode.notes.data.Notes.TextNote;
|
||||||
|
import net.micode.notes.tool.ResourceParser.NoteBgResources;
|
||||||
|
|
||||||
|
|
||||||
|
public class WorkingNote {
|
||||||
|
// Note for the working note
|
||||||
|
private Note mNote;
|
||||||
|
// Note Id
|
||||||
|
private long mNoteId;
|
||||||
|
// Note content
|
||||||
|
private String mContent;
|
||||||
|
// Note mode
|
||||||
|
private int mMode;
|
||||||
|
|
||||||
|
private long mAlertDate;
|
||||||
|
|
||||||
|
private long mModifiedDate;
|
||||||
|
|
||||||
|
private int mBgColorId;
|
||||||
|
|
||||||
|
private int mWidgetId;
|
||||||
|
|
||||||
|
private int mWidgetType;
|
||||||
|
|
||||||
|
private long mFolderId;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
private static final String TAG = "WorkingNote";
|
||||||
|
|
||||||
|
private boolean mIsDeleted;
|
||||||
|
|
||||||
|
private NoteSettingChangedListener mNoteSettingStatusListener;
|
||||||
|
|
||||||
|
public static final String[] DATA_PROJECTION = new String[] {
|
||||||
|
DataColumns.ID,
|
||||||
|
DataColumns.CONTENT,
|
||||||
|
DataColumns.MIME_TYPE,
|
||||||
|
DataColumns.DATA1,
|
||||||
|
DataColumns.DATA2,
|
||||||
|
DataColumns.DATA3,
|
||||||
|
DataColumns.DATA4,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final String[] NOTE_PROJECTION = new String[] {
|
||||||
|
NoteColumns.PARENT_ID,
|
||||||
|
NoteColumns.ALERTED_DATE,
|
||||||
|
NoteColumns.BG_COLOR_ID,
|
||||||
|
NoteColumns.WIDGET_ID,
|
||||||
|
NoteColumns.WIDGET_TYPE,
|
||||||
|
NoteColumns.MODIFIED_DATE
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int DATA_ID_COLUMN = 0;
|
||||||
|
|
||||||
|
private static final int DATA_CONTENT_COLUMN = 1;
|
||||||
|
|
||||||
|
private static final int DATA_MIME_TYPE_COLUMN = 2;
|
||||||
|
|
||||||
|
private static final int DATA_MODE_COLUMN = 3;
|
||||||
|
|
||||||
|
private static final int NOTE_PARENT_ID_COLUMN = 0;
|
||||||
|
|
||||||
|
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
|
||||||
|
|
||||||
|
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
|
||||||
|
|
||||||
|
private static final int NOTE_WIDGET_ID_COLUMN = 3;
|
||||||
|
|
||||||
|
private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
|
||||||
|
|
||||||
|
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
|
||||||
|
|
||||||
|
// New note construct
|
||||||
|
private WorkingNote(Context context, long folderId) {
|
||||||
|
mContext = context;
|
||||||
|
mAlertDate = 0;
|
||||||
|
mModifiedDate = System.currentTimeMillis();
|
||||||
|
mFolderId = folderId;
|
||||||
|
mNote = new Note();
|
||||||
|
mNoteId = 0;
|
||||||
|
mIsDeleted = false;
|
||||||
|
mMode = 0;
|
||||||
|
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Existing note construct
|
||||||
|
private WorkingNote(Context context, long noteId, long folderId) {
|
||||||
|
mContext = context;
|
||||||
|
mNoteId = noteId;
|
||||||
|
mFolderId = folderId;
|
||||||
|
mIsDeleted = false;
|
||||||
|
mNote = new Note();
|
||||||
|
loadNote();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadNote() {
|
||||||
|
Cursor cursor = mContext.getContentResolver().query(
|
||||||
|
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
|
||||||
|
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
|
||||||
|
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
|
||||||
|
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
|
||||||
|
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
|
||||||
|
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "No note with id:" + mNoteId);
|
||||||
|
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
|
||||||
|
}
|
||||||
|
loadNoteData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadNoteData() {
|
||||||
|
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
|
||||||
|
DataColumns.NOTE_ID + "=?", new String[] {
|
||||||
|
String.valueOf(mNoteId)
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
|
||||||
|
if (DataConstants.NOTE.equals(type)) {
|
||||||
|
mContent = cursor.getString(DATA_CONTENT_COLUMN);
|
||||||
|
mMode = cursor.getInt(DATA_MODE_COLUMN);
|
||||||
|
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
|
||||||
|
} else if (DataConstants.CALL_NOTE.equals(type)) {
|
||||||
|
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Wrong note type with type:" + type);
|
||||||
|
}
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "No data with id:" + mNoteId);
|
||||||
|
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
|
||||||
|
int widgetType, int defaultBgColorId) {
|
||||||
|
WorkingNote note = new WorkingNote(context, folderId);
|
||||||
|
note.setBgColorId(defaultBgColorId);
|
||||||
|
note.setWidgetId(widgetId);
|
||||||
|
note.setWidgetType(widgetType);
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorkingNote load(Context context, long id) {
|
||||||
|
return new WorkingNote(context, id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean saveNote() {
|
||||||
|
if (isWorthSaving()) {
|
||||||
|
if (!existInDatabase()) {
|
||||||
|
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
|
||||||
|
Log.e(TAG, "Create new note fail with id:" + mNoteId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mNote.syncNote(mContext, mNoteId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update widget content if there exist any widget of this note
|
||||||
|
*/
|
||||||
|
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
|
||||||
|
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
|
||||||
|
&& mNoteSettingStatusListener != null) {
|
||||||
|
mNoteSettingStatusListener.onWidgetChanged();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existInDatabase() {
|
||||||
|
return mNoteId > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWorthSaving() {
|
||||||
|
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|
||||||
|
|| (existInDatabase() && !mNote.isLocalModified())) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
|
||||||
|
mNoteSettingStatusListener = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlertDate(long date, boolean set) {
|
||||||
|
if (date != mAlertDate) {
|
||||||
|
mAlertDate = date;
|
||||||
|
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
|
||||||
|
}
|
||||||
|
if (mNoteSettingStatusListener != null) {
|
||||||
|
mNoteSettingStatusListener.onClockAlertChanged(date, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markDeleted(boolean mark) {
|
||||||
|
mIsDeleted = mark;
|
||||||
|
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
|
||||||
|
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
|
||||||
|
mNoteSettingStatusListener.onWidgetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBgColorId(int id) {
|
||||||
|
if (id != mBgColorId) {
|
||||||
|
mBgColorId = id;
|
||||||
|
if (mNoteSettingStatusListener != null) {
|
||||||
|
mNoteSettingStatusListener.onBackgroundColorChanged();
|
||||||
|
}
|
||||||
|
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckListMode(int mode) {
|
||||||
|
if (mMode != mode) {
|
||||||
|
if (mNoteSettingStatusListener != null) {
|
||||||
|
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
|
||||||
|
}
|
||||||
|
mMode = mode;
|
||||||
|
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidgetType(int type) {
|
||||||
|
if (type != mWidgetType) {
|
||||||
|
mWidgetType = type;
|
||||||
|
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidgetId(int id) {
|
||||||
|
if (id != mWidgetId) {
|
||||||
|
mWidgetId = id;
|
||||||
|
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorkingText(String text) {
|
||||||
|
if (!TextUtils.equals(mContent, text)) {
|
||||||
|
mContent = text;
|
||||||
|
mNote.setTextData(DataColumns.CONTENT, mContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void convertToCallNote(String phoneNumber, long callDate) {
|
||||||
|
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
|
||||||
|
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
|
||||||
|
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasClockAlert() {
|
||||||
|
return (mAlertDate > 0 ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return mContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAlertDate() {
|
||||||
|
return mAlertDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getModifiedDate() {
|
||||||
|
return mModifiedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBgColorResId() {
|
||||||
|
return NoteBgResources.getNoteBgResource(mBgColorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBgColorId() {
|
||||||
|
return mBgColorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTitleBgResId() {
|
||||||
|
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCheckListMode() {
|
||||||
|
return mMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getNoteId() {
|
||||||
|
return mNoteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFolderId() {
|
||||||
|
return mFolderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidgetId() {
|
||||||
|
return mWidgetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidgetType() {
|
||||||
|
return mWidgetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface NoteSettingChangedListener {
|
||||||
|
/**
|
||||||
|
* Called when the background color of current note has just changed
|
||||||
|
*/
|
||||||
|
void onBackgroundColorChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when user set clock
|
||||||
|
*/
|
||||||
|
void onClockAlertChanged(long date, boolean set);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call when user create note from widget
|
||||||
|
*/
|
||||||
|
void onWidgetChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call when switch between check list mode and normal mode
|
||||||
|
* @param oldMode is previous mode before change
|
||||||
|
* @param newMode is new mode
|
||||||
|
*/
|
||||||
|
void onCheckListModeChanged(int oldMode, int newMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
// Apache许可证协议
|
||||||
|
|
||||||
|
package net.micode.notes.ui;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.DialogInterface.OnClickListener;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
|
import net.micode.notes.R;
|
||||||
|
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
|
||||||
|
|
||||||
|
private Calendar mDate = Calendar.getInstance();
|
||||||
|
private boolean mIs24HourView;
|
||||||
|
private OnDateTimeSetListener mOnDateTimeSetListener;
|
||||||
|
private DateTimePicker mDateTimePicker;
|
||||||
|
|
||||||
|
public interface OnDateTimeSetListener {
|
||||||
|
void OnDateTimeSet(AlertDialog dialog, long date);
|
||||||
|
}
|
||||||
|
// 实现了一个 OnDateTimeSetListener 接口,该接口定义了一个 OnDateTimeSet() 方法,用于在用户选择日期时间后通知调用者。
|
||||||
|
|
||||||
|
public DateTimePickerDialog(Context context, long date) {
|
||||||
|
super(context);
|
||||||
|
mDateTimePicker = new DateTimePicker(context);
|
||||||
|
setView(mDateTimePicker);
|
||||||
|
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
|
||||||
|
public void onDateTimeChanged(DateTimePicker view, int year, int month,
|
||||||
|
int dayOfMonth, int hourOfDay, int minute) {
|
||||||
|
mDate.set(Calendar.YEAR, year);
|
||||||
|
mDate.set(Calendar.MONTH, month);
|
||||||
|
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||||
|
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
|
||||||
|
mDate.set(Calendar.MINUTE, minute);
|
||||||
|
updateTitle(mDate.getTimeInMillis());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 包含了一个 DateTimePicker 控件,用于让用户选择日期和时间。通过 mDateTimePicker.setOnDateTimeChangedListener() 方法,当用户选择日期和时间时,将更新 mDate 的值,并使用 updateTitle() 方法更新对话框的标题。
|
||||||
|
mDate.setTimeInMillis(date);
|
||||||
|
mDate.set(Calendar.SECOND, 0);
|
||||||
|
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
|
||||||
|
setButton(context.getString(R.string.datetime_dialog_ok), this);
|
||||||
|
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
|
||||||
|
set24HourView(DateFormat.is24HourFormat(this.getContext()));
|
||||||
|
updateTitle(mDate.getTimeInMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set24HourView(boolean is24HourView) {
|
||||||
|
mIs24HourView = is24HourView;
|
||||||
|
}
|
||||||
|
//set24HourView() 方法,用于设置日期时间选择器是否为 24 小时格式。默认情况下,该值将由系统的设置决定。
|
||||||
|
|
||||||
|
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
|
||||||
|
mOnDateTimeSetListener = callBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTitle(long date) {
|
||||||
|
int flag =
|
||||||
|
DateUtils.FORMAT_SHOW_YEAR |
|
||||||
|
DateUtils.FORMAT_SHOW_DATE |
|
||||||
|
DateUtils.FORMAT_SHOW_TIME;
|
||||||
|
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
|
||||||
|
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick(DialogInterface arg0, int arg1) {
|
||||||
|
if (mOnDateTimeSetListener != null) {
|
||||||
|
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 重写了 onClick() 方法,以便在用户点击“确定”按钮后,将用户选择的日期时间作为参数传递到 OnDateTimeSet() 方法中,从而通知调用者。
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,200 @@
|
|||||||
|
// Apache许可证协议
|
||||||
|
|
||||||
|
package net.micode.notes.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.Selection;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.URLSpan;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.MenuItem.OnMenuItemClickListener;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import net.micode.notes.R;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NoteEditText extends EditText {
|
||||||
|
private static final String TAG = "NoteEditText";
|
||||||
|
private int mIndex;
|
||||||
|
private int mSelectionStartBeforeDelete;
|
||||||
|
|
||||||
|
private static final String SCHEME_TEL = "tel:" ;
|
||||||
|
private static final String SCHEME_HTTP = "http:" ;
|
||||||
|
private static final String SCHEME_EMAIL = "mailto:" ;
|
||||||
|
|
||||||
|
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
|
||||||
|
static {
|
||||||
|
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
|
||||||
|
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
|
||||||
|
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call by the {@link NoteEditActivity} to delete or add edit text
|
||||||
|
*/
|
||||||
|
public interface OnTextViewChangeListener {
|
||||||
|
/**
|
||||||
|
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
|
||||||
|
* and the text is null
|
||||||
|
*/
|
||||||
|
void onEditTextDelete(int index, String text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
|
||||||
|
* happen
|
||||||
|
*/
|
||||||
|
void onEditTextEnter(int index, String text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide or show item option when text change
|
||||||
|
*/
|
||||||
|
void onTextChange(int index, boolean hasText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnTextViewChangeListener mOnTextViewChangeListener;
|
||||||
|
|
||||||
|
public NoteEditText(Context context) {
|
||||||
|
super(context, null);
|
||||||
|
mIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
mIndex = index;
|
||||||
|
}//用于设置控件在父容器中的位置索引
|
||||||
|
|
||||||
|
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
|
||||||
|
mOnTextViewChangeListener = listener;
|
||||||
|
}//用于设置文本变化的监听器
|
||||||
|
|
||||||
|
public NoteEditText(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs, android.R.attr.editTextStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
|
||||||
|
int x = (int) event.getX();
|
||||||
|
int y = (int) event.getY();
|
||||||
|
x -= getTotalPaddingLeft();
|
||||||
|
y -= getTotalPaddingTop();
|
||||||
|
x += getScrollX();
|
||||||
|
y += getScrollY();
|
||||||
|
|
||||||
|
Layout layout = getLayout();
|
||||||
|
int line = layout.getLineForVertical(y);
|
||||||
|
int off = layout.getOffsetForHorizontal(line, x);
|
||||||
|
Selection.setSelection(getText(), off);
|
||||||
|
break;
|
||||||
|
}//实现了点击控件后,将光标移动到点击位置的功能
|
||||||
|
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_ENTER:
|
||||||
|
if (mOnTextViewChangeListener != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyEvent.KEYCODE_DEL:
|
||||||
|
mSelectionStartBeforeDelete = getSelectionStart();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
switch(keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_DEL:
|
||||||
|
if (mOnTextViewChangeListener != null) {
|
||||||
|
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
|
||||||
|
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "OnTextViewChangeListener was not seted");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyEvent.KEYCODE_ENTER:
|
||||||
|
if (mOnTextViewChangeListener != null) {
|
||||||
|
int selectionStart = getSelectionStart();
|
||||||
|
String text = getText().subSequence(selectionStart, length()).toString();
|
||||||
|
setText(getText().subSequence(0, selectionStart));
|
||||||
|
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "OnTextViewChangeListener was not seted");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return super.onKeyUp(keyCode, event);
|
||||||
|
}
|
||||||
|
// onKeyDown和onKeyUp方法实现了按下和松开键盘按键时的响应。
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
|
||||||
|
if (mOnTextViewChangeListener != null) {
|
||||||
|
mOnTextViewChangeListener.onTextChange(mIndex, focused || !TextUtils.isEmpty(getText()));
|
||||||
|
}
|
||||||
|
super.onFocusChanged(focused, direction, previouslyFocusedRect);
|
||||||
|
}//控件获得或失去焦点时触发,将焦点状态通知给监听器。
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreateContextMenu(ContextMenu menu) {
|
||||||
|
if (getText() instanceof Spanned) {
|
||||||
|
int selStart = getSelectionStart();
|
||||||
|
int selEnd = getSelectionEnd();
|
||||||
|
|
||||||
|
int min = Math.min(selStart, selEnd);
|
||||||
|
int max = Math.max(selStart, selEnd);
|
||||||
|
|
||||||
|
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
|
||||||
|
if (urls.length == 1) {
|
||||||
|
int defaultResId = 0;
|
||||||
|
for(String schema: sSchemaActionResMap.keySet()) {
|
||||||
|
if(urls[0].getURL().indexOf(schema) >= 0) {
|
||||||
|
defaultResId = sSchemaActionResMap.get(schema);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultResId == 0) {
|
||||||
|
defaultResId = R.string.note_link_other;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
|
||||||
|
new OnMenuItemClickListener() {
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
// goto a new intent
|
||||||
|
urls[0].onClick(NoteEditText.this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onCreateContextMenu(menu);
|
||||||
|
}
|
||||||
|
// 创建上下文菜单,当用户长按控件中的链接时,根据链接的协议类型创建不同的菜单项,点击菜单项后跳转到相应的链接。
|
||||||
|
}
|
||||||
@ -0,0 +1,210 @@
|
|||||||
|
// Apache许可证协议
|
||||||
|
|
||||||
|
package net.micode.notes.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import net.micode.notes.data.Contact;
|
||||||
|
import net.micode.notes.data.Notes;
|
||||||
|
import net.micode.notes.data.Notes.NoteColumns;
|
||||||
|
import net.micode.notes.tool.DataUtils;
|
||||||
|
|
||||||
|
|
||||||
|
public class NoteItemData {
|
||||||
|
static final String [] PROJECTION = new String [] {
|
||||||
|
NoteColumns.ID,
|
||||||
|
NoteColumns.ALERTED_DATE,
|
||||||
|
NoteColumns.BG_COLOR_ID,
|
||||||
|
NoteColumns.CREATED_DATE,
|
||||||
|
NoteColumns.HAS_ATTACHMENT,
|
||||||
|
NoteColumns.MODIFIED_DATE,
|
||||||
|
NoteColumns.NOTES_COUNT,
|
||||||
|
NoteColumns.PARENT_ID,
|
||||||
|
NoteColumns.SNIPPET,
|
||||||
|
NoteColumns.TYPE,
|
||||||
|
NoteColumns.WIDGET_ID,
|
||||||
|
NoteColumns.WIDGET_TYPE,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int ID_COLUMN = 0;
|
||||||
|
private static final int ALERTED_DATE_COLUMN = 1;
|
||||||
|
private static final int BG_COLOR_ID_COLUMN = 2;
|
||||||
|
private static final int CREATED_DATE_COLUMN = 3;
|
||||||
|
private static final int HAS_ATTACHMENT_COLUMN = 4;
|
||||||
|
private static final int MODIFIED_DATE_COLUMN = 5;
|
||||||
|
private static final int NOTES_COUNT_COLUMN = 6;
|
||||||
|
private static final int PARENT_ID_COLUMN = 7;
|
||||||
|
private static final int SNIPPET_COLUMN = 8;
|
||||||
|
private static final int TYPE_COLUMN = 9;
|
||||||
|
private static final int WIDGET_ID_COLUMN = 10;
|
||||||
|
private static final int WIDGET_TYPE_COLUMN = 11;
|
||||||
|
|
||||||
|
private long mId;
|
||||||
|
private long mAlertDate;
|
||||||
|
private int mBgColorId;
|
||||||
|
private long mCreatedDate;
|
||||||
|
private boolean mHasAttachment;
|
||||||
|
private long mModifiedDate;
|
||||||
|
private int mNotesCount;
|
||||||
|
private long mParentId;
|
||||||
|
private String mSnippet;
|
||||||
|
private int mType;
|
||||||
|
private int mWidgetId;
|
||||||
|
private int mWidgetType;
|
||||||
|
private String mName;
|
||||||
|
private String mPhoneNumber;
|
||||||
|
|
||||||
|
private boolean mIsLastItem;
|
||||||
|
private boolean mIsFirstItem;
|
||||||
|
private boolean mIsOnlyOneItem;
|
||||||
|
private boolean mIsOneNoteFollowingFolder;
|
||||||
|
private boolean mIsMultiNotesFollowingFolder;
|
||||||
|
|
||||||
|
public NoteItemData(Context context, Cursor cursor) {
|
||||||
|
mId = cursor.getLong(ID_COLUMN);
|
||||||
|
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
|
||||||
|
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
|
||||||
|
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
|
||||||
|
mHasAttachment = cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0;
|
||||||
|
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
|
||||||
|
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
|
||||||
|
mParentId = cursor.getLong(PARENT_ID_COLUMN);
|
||||||
|
mSnippet = cursor.getString(SNIPPET_COLUMN);
|
||||||
|
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
|
||||||
|
NoteEditActivity.TAG_UNCHECKED, "");
|
||||||
|
mType = cursor.getInt(TYPE_COLUMN);
|
||||||
|
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
|
||||||
|
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
|
||||||
|
|
||||||
|
mPhoneNumber = "";
|
||||||
|
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
|
||||||
|
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
|
||||||
|
if (!TextUtils.isEmpty(mPhoneNumber)) {
|
||||||
|
mName = Contact.getContact(context, mPhoneNumber);
|
||||||
|
if (mName == null) {
|
||||||
|
mName = mPhoneNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mName == null) {
|
||||||
|
mName = "";
|
||||||
|
}
|
||||||
|
checkPosition(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPosition(Cursor cursor) {
|
||||||
|
mIsLastItem = cursor.isLast();
|
||||||
|
mIsFirstItem = cursor.isFirst();
|
||||||
|
mIsOnlyOneItem = (cursor.getCount() == 1);
|
||||||
|
mIsMultiNotesFollowingFolder = false;
|
||||||
|
mIsOneNoteFollowingFolder = false;
|
||||||
|
|
||||||
|
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
|
||||||
|
int position = cursor.getPosition();
|
||||||
|
if (cursor.moveToPrevious()) {
|
||||||
|
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|
||||||
|
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
|
||||||
|
if (cursor.getCount() > (position + 1)) {
|
||||||
|
mIsMultiNotesFollowingFolder = true;
|
||||||
|
} else {
|
||||||
|
mIsOneNoteFollowingFolder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cursor.moveToNext()) {
|
||||||
|
throw new IllegalStateException("cursor move to previous but can't move back");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOneFollowingFolder() {
|
||||||
|
return mIsOneNoteFollowingFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiFollowingFolder() {
|
||||||
|
return mIsMultiNotesFollowingFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLast() {
|
||||||
|
return mIsLastItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCallName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFirst() {
|
||||||
|
return mIsFirstItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSingle() {
|
||||||
|
return mIsOnlyOneItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAlertDate() {
|
||||||
|
return mAlertDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCreatedDate() {
|
||||||
|
return mCreatedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAttachment() {
|
||||||
|
return mHasAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getModifiedDate() {
|
||||||
|
return mModifiedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBgColorId() {
|
||||||
|
return mBgColorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getParentId() {
|
||||||
|
return mParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNotesCount() {
|
||||||
|
return mNotesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFolderId () {
|
||||||
|
return mParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidgetType() {
|
||||||
|
return mWidgetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidgetId() {
|
||||||
|
return mWidgetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSnippet() {
|
||||||
|
return mSnippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAlert() {
|
||||||
|
return (mAlertDate > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCallRecord() {
|
||||||
|
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getNoteType(Cursor cursor) {
|
||||||
|
return cursor.getInt(TYPE_COLUMN);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,171 @@
|
|||||||
|
// Apache许可证协议
|
||||||
|
|
||||||
|
package net.micode.notes.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CursorAdapter;
|
||||||
|
|
||||||
|
import net.micode.notes.data.Notes;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|
||||||
|
public class NotesListAdapter extends CursorAdapter {
|
||||||
|
private static final String TAG = "NotesListAdapter";
|
||||||
|
private Context mContext;
|
||||||
|
private HashMap<Integer, Boolean> mSelectedIndex;
|
||||||
|
private int mNotesCount;
|
||||||
|
private boolean mChoiceMode;
|
||||||
|
|
||||||
|
public static class AppWidgetAttribute {
|
||||||
|
public int widgetId;
|
||||||
|
public int widgetType;
|
||||||
|
};
|
||||||
|
|
||||||
|
public NotesListAdapter(Context context) {
|
||||||
|
super(context, null);
|
||||||
|
mSelectedIndex = new HashMap<>();
|
||||||
|
mContext = context;
|
||||||
|
mNotesCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return new NotesListItem(context);
|
||||||
|
}//创建一个新的笔记列表项视图,即 NotesListItem。
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
if (view instanceof NotesListItem) {
|
||||||
|
NoteItemData itemData = new NoteItemData(context, cursor);
|
||||||
|
((NotesListItem) view).bind(context, itemData, mChoiceMode,
|
||||||
|
isSelectedItem(cursor.getPosition()));
|
||||||
|
}
|
||||||
|
}//将笔记数据绑定到给定的笔记列表项视图中。
|
||||||
|
|
||||||
|
public void setCheckedItem(final int position, final boolean checked) {
|
||||||
|
mSelectedIndex.put(position, checked);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}//设置给定位置的笔记是否被选中,并通知适配器数据已更改。
|
||||||
|
|
||||||
|
public boolean isInChoiceMode() {
|
||||||
|
return mChoiceMode;
|
||||||
|
}//返回是否处于选择笔记的模式。
|
||||||
|
|
||||||
|
public void setChoiceMode(boolean mode) {
|
||||||
|
mSelectedIndex.clear();
|
||||||
|
mChoiceMode = mode;
|
||||||
|
}//设置选择笔记的模式,并清空 mSelectedIndex。
|
||||||
|
|
||||||
|
public void selectAll(boolean checked) {
|
||||||
|
Cursor cursor = getCursor();
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
if (cursor.moveToPosition(i)) {
|
||||||
|
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {
|
||||||
|
setCheckedItem(i, checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}//选择所有笔记。
|
||||||
|
|
||||||
|
public HashSet<Long> getSelectedItemIds() {
|
||||||
|
HashSet<Long> itemSet = new HashSet<Long>();
|
||||||
|
for (Integer position : mSelectedIndex.keySet()) {
|
||||||
|
if (mSelectedIndex.get(position) == true) {
|
||||||
|
Long id = getItemId(position);
|
||||||
|
if (id == Notes.ID_ROOT_FOLDER) {
|
||||||
|
Log.d(TAG, "Wrong item id, should not happen");
|
||||||
|
} else {
|
||||||
|
itemSet.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemSet;
|
||||||
|
}//返回选定的笔记的 ID。
|
||||||
|
|
||||||
|
|
||||||
|
public HashSet<AppWidgetAttribute> getSelectedWidget() {
|
||||||
|
HashSet<AppWidgetAttribute> itemSet = new HashSet<>();
|
||||||
|
for (Integer position : mSelectedIndex.keySet()) {
|
||||||
|
if (Boolean.TRUE.equals(mSelectedIndex.get(position))) {
|
||||||
|
Cursor c = (Cursor) getItem(position);
|
||||||
|
if (c != null) {
|
||||||
|
AppWidgetAttribute widget = new AppWidgetAttribute();
|
||||||
|
NoteItemData item = new NoteItemData(mContext, c);
|
||||||
|
widget.widgetId = item.getWidgetId();
|
||||||
|
widget.widgetType = item.getWidgetType();
|
||||||
|
itemSet.add(widget);
|
||||||
|
/**
|
||||||
|
* Don't close cursor here, only the adapter could close it
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Invalid cursor");
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return itemSet;
|
||||||
|
}//返回选定的小部件信息。
|
||||||
|
|
||||||
|
public int getSelectedCount() {
|
||||||
|
Collection<Boolean> values = mSelectedIndex.values();
|
||||||
|
if (null == values) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Iterator<Boolean> iter = values.iterator();
|
||||||
|
int count = 0;
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
if (iter.next()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}//返回选定笔记的数量。
|
||||||
|
|
||||||
|
public boolean isAllSelected() {
|
||||||
|
int checkedCount = getSelectedCount();
|
||||||
|
return (checkedCount != 0 && checkedCount == mNotesCount);
|
||||||
|
}//返回是否已经选择了所有笔记。
|
||||||
|
|
||||||
|
public boolean isSelectedItem(final int position) {
|
||||||
|
if (null == mSelectedIndex.get(position)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mSelectedIndex.get(position);
|
||||||
|
}//返回给定位置的笔记是否被选中。
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onContentChanged() {
|
||||||
|
super.onContentChanged();
|
||||||
|
calcNotesCount();
|
||||||
|
}//当笔记数据发生变化时,重新计算笔记数量。
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeCursor(Cursor cursor) {
|
||||||
|
super.changeCursor(cursor);
|
||||||
|
calcNotesCount();
|
||||||
|
}//更改笔记数据游标时,重新计算笔记数量。
|
||||||
|
|
||||||
|
private void calcNotesCount() {
|
||||||
|
mNotesCount = 0;
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
Cursor c = (Cursor) getItem(i);
|
||||||
|
if (c != null) {
|
||||||
|
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
|
||||||
|
mNotesCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Invalid cursor");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}//计算笔记数量。
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
// Apache许可证协议
|
||||||
|
package net.micode.notes.widget;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import net.micode.notes.R;
|
||||||
|
import net.micode.notes.data.Notes;
|
||||||
|
import net.micode.notes.tool.ResourceParser;
|
||||||
|
|
||||||
|
|
||||||
|
public class NoteWidgetProvider_2x extends NoteWidgetProvider { // 2x2大小的窗口小部件
|
||||||
|
@Override // 重写窗口小部件中的更新函数
|
||||||
|
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||||
|
super.update(context, appWidgetManager, appWidgetIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // 重写窗口小部件中的获取布局ID函数
|
||||||
|
protected int getLayoutId() {
|
||||||
|
return R.layout.widget_2x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // 重写窗口小部件中的获取资源ID函数
|
||||||
|
protected int getBgResourceId(int bgId) {
|
||||||
|
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // 重写窗口小部件中的获取宽度类型函数
|
||||||
|
protected int getWidgetType() {
|
||||||
|
return Notes.TYPE_WIDGET_2X;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
// Apache许可证协议
|
||||||
|
|
||||||
|
package net.micode.notes.widget;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import net.micode.notes.R;
|
||||||
|
import net.micode.notes.data.Notes;
|
||||||
|
import net.micode.notes.tool.ResourceParser;
|
||||||
|
|
||||||
|
|
||||||
|
public class NoteWidgetProvider_4x extends NoteWidgetProvider { // 4x4大小的窗口小部件
|
||||||
|
@Override // 重写窗口小部件中的更新函数
|
||||||
|
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||||
|
super.update(context, appWidgetManager, appWidgetIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // 重写窗口小部件中的获取布局ID函数
|
||||||
|
protected int getLayoutId() {
|
||||||
|
return R.layout.widget_4x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // 重写窗口小部件中的获取资源ID函数
|
||||||
|
protected int getBgResourceId(int bgId) {
|
||||||
|
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // 重写窗口小部件中的获取宽度类型函数
|
||||||
|
protected int getWidgetType() {
|
||||||
|
return Notes.TYPE_WIDGET_4X;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,2 +0,0 @@
|
|||||||
#Sun Apr 02 08:57:27 CST 2023
|
|
||||||
gradle.version=7.5
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
useLibrary 'org.apache.http.legacy'
|
|
||||||
compileSdkVersion 33
|
|
||||||
buildToolsVersion "33.0.2"
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "net.micode.notes"
|
|
||||||
minSdkVersion 14
|
|
||||||
targetSdkVersion 33
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled false
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||