Compare commits

...

44 Commits

Author SHA1 Message Date
王为溪 98c9b4bb33 王为溪
9 months ago
王为溪 241d49a9d4 王为溪
9 months ago
林名赫 3bbbf05630 ngp
9 months ago
周竞航 ca40f52ae6 zjh
9 months ago
林名赫 c7b4aa165a nangua
9 months ago
林名赫 22d6dd32ce nangua
9 months ago
王为溪 98cff05288 王为溪
9 months ago
王为溪 71b17418e6 王为溪
9 months ago
王为溪 e31b95c2a5 王为溪
9 months ago
王为溪 c28c985b5d 王为溪
9 months ago
王为溪 cf2a4c9514 王为溪
9 months ago
王为溪 23c08c13c2 王为溪
9 months ago
王为溪 90fd73f342 王为溪
9 months ago
林名赫 aa85a8f262 model_last
9 months ago
林名赫 615cb32740 last
9 months ago
王为溪 d0f4edf5dd 王为溪
9 months ago
林名赫 5e9be860af 3.1nangua
11 months ago
林名赫 b577a273e7 3.1nangua
11 months ago
王为溪 e45c9d7afd 王为溪
11 months ago
王为溪 fc86662459 王为溪
11 months ago
林名赫 4cf5799b61 nangua
11 months ago
林名赫 f9ca1bb803 nangua
11 months ago
王为溪 c730d49cfb 王为溪
11 months ago
王为溪 1dc07b071a Merge branch 'main' of https://bdgit.educoder.net/p7yqafb3h/minote
11 months ago
王为溪 24f1ffb3ff 王为溪
11 months ago
周竞航 84c24aa3fc zhou
11 months ago
周竞航 a3ea6b9412 Merge branch 'main' of https://bdgit.educoder.net/p7yqafb3h/minote
11 months ago
周竞航 5e20df94f9 zhou
11 months ago
pc6x7rfso 0b15aec2d4 Delete 'Note.java'
11 months ago
pc6x7rfso 183b31c8f1 Delete 'NoteEditActivity.java'
11 months ago
pc6x7rfso 5e4db94d50 Delete 'NoteEditText.java'
11 months ago
pc6x7rfso d9600f08af Delete 'SqlData.java'
11 months ago
pc6x7rfso c6d8510741 Delete 'WorkingNote.java'
11 months ago
周竞航 bb2bdc8947 zhou
11 months ago
周竞航 2f5dc0d44b 123.txt
11 months ago
王为溪 d98ddd1915 王为溪
11 months ago
王为溪 f6dbd9ec04 王为溪
11 months ago
林名赫 5a37ef8357 nangua
11 months ago
周竞航 7717d9a6b4 anhang
11 months ago
林名赫 f5b0a7742f nangua提交
11 months ago
林名赫 108680ba1c test
11 months ago
p7yqafb3h 63e3c04955 Merge pull request '主' (#2) from master into main
11 months ago
林名赫 473db86a62 test
12 months ago
林名赫 7acc38963f 111.txt
12 months ago

@ -1 +1,3 @@
111 111
222
333

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@ -0,0 +1 @@
Subproject commit 683b29d4a90ccf817ba9f83034dfdb2c707e7fe9

@ -0,0 +1,199 @@
/*
* 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;
// 该类继承自Activity用于处理闹钟提醒相关的操作包括显示提醒对话框、播放闹钟声音等
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
// 存储笔记的ID
private long mNoteId;
// 存储笔记的摘要信息
private String mSnippet;
// 摘要信息显示的最大长度
private static final int SNIPPET_PREW_MAX_LEN = 60;
// 用于播放闹钟声音的MediaPlayer对象
MediaPlayer mPlayer;
// 当Activity创建时调用的方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 不显示Activity的标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
final Window win = getWindow();
// 即使屏幕被锁定也显示该Activity
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);
}
// 获取启动该Activity的Intent对象
Intent intent = getIntent();
try {
// 从Intent的数据中获取笔记的ID
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 根据笔记ID从内容提供者中获取笔记的摘要信息
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;
}
// 创建MediaPlayer对象
mPlayer = new MediaPlayer();
// 检查笔记是否在数据库中可见且为普通笔记类型
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
// 显示提醒对话框
showActionDialog();
// 播放闹钟声音
playAlarmSound();
} else {
// 如果笔记不符合条件关闭该Activity
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);
// 根据静音模式设置设置MediaPlayer的音频流类型
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置MediaPlayer的数据源为闹钟铃声的Uri
mPlayer.setDataSource(this, url);
// 准备MediaPlayer
mPlayer.prepare();
// 设置MediaPlayer循环播放
mPlayer.setLooping(true);
// 开始播放闹钟声音
mPlayer.start();
} catch (IllegalArgumentException e) {
// 处理异常情况,打印异常堆栈信息
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 显示提醒对话框的方法
private void showActionDialog() {
// 创建AlertDialog构建器
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框的标题为应用名称
dialog.setTitle(R.string.app_name);
// 设置对话框的消息为笔记的摘要信息
dialog.setMessage(mSnippet);
// 设置对话框的确定按钮点击后触发onClick方法
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 intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动笔记编辑页面
startActivity(intent);
break;
default:
break;
}
}
// 处理对话框关闭事件的方法
public void onDismiss(DialogInterface dialog) {
// 停止播放闹钟声音
stopAlarmSound();
// 关闭该Activity
finish();
}
// 停止播放闹钟声音的方法
private void stopAlarmSound() {
if (mPlayer != null) {
// 停止MediaPlayer的播放
mPlayer.stop();
// 释放MediaPlayer资源
mPlayer.release();
mPlayer = null;
}
}
}

@ -0,0 +1,78 @@
/*
* 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.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
// 该类继承自BroadcastReceiver用于初始化闹钟提醒
public class AlarmInitReceiver extends BroadcastReceiver {
// 查询数据库时需要的列名
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
// 数据库查询结果中ID列的索引
private static final int COLUMN_ID = 0;
// 数据库查询结果中提醒日期列的索引
private static final int COLUMN_ALERTED_DATE = 1;
// 当接收到广播时调用的方法
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前时间
long currentDate = System.currentTimeMillis();
// 查询数据库,获取所有提醒日期在当前时间之后且为普通笔记类型的记录
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
null);
if (c != null) {
if (c.moveToFirst()) {
do {
// 获取当前记录的提醒日期
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个Intent对象用于启动AlarmReceiver广播接收器
Intent sender = new Intent(context, AlarmReceiver.class);
// 设置Intent的数据为笔记的Uri
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建一个PendingIntent对象用于在提醒日期触发广播
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取闹钟管理服务
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 设置闹钟在提醒日期触发PendingIntent
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
// 关闭游标
c.close();
}
}
}

@ -0,0 +1,43 @@
/*
* 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;
/**
* AlarmReceiver 广广 AlarmAlertActivity
* BroadcastReceiver广
*/
public class AlarmReceiver extends BroadcastReceiver {
/**
* 广
*
* @param context
* @param intent 广广
*/
@Override
public void onReceive(Context context, Intent intent) {
// 设置意图的目标类为 AlarmAlertActivity即要启动的活动类
intent.setClass(context, AlarmAlertActivity.class);
// 添加 FLAG_ACTIVITY_NEW_TASK 标志,用于在新的任务栈中启动活动
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 使用上下文对象启动活动
context.startActivity(intent);
}
}

@ -0,0 +1,712 @@
/*
* 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.
*/
// 包声明,表明该类位于 net.micode.notes.ui 包下
package net.micode.notes.ui;
// 导入日期格式符号类,用于获取 AM/PM 字符串
import java.text.DateFormatSymbols;
// 导入日历类,用于处理日期和时间
import java.util.Calendar;
// 导入资源类,用于访问应用中的资源
import net.micode.notes.R;
// 导入 Android 上下文类,用于访问应用的资源和类
import android.content.Context;
// 导入 Android 日期格式化类,用于获取系统的日期和时间格式设置
import android.text.format.DateFormat;
// 导入 Android 视图类,是所有 UI 组件的基类
import android.view.View;
// 导入 Android 帧布局类,用于提供一个简单的布局容器
import android.widget.FrameLayout;
// 导入 Android 数字选择器类,用于让用户选择数字
import android.widget.NumberPicker;
/**
* DateTimePicker FrameLayout
*
*/
public class DateTimePicker extends FrameLayout {
// 默认的启用状态,默认为启用
private static final boolean DEFAULT_ENABLE_STATE = true;
// 半天的小时数
private static final int HOURS_IN_HALF_DAY = 12;
// 一整天的小时数
private static final int HOURS_IN_ALL_DAY = 24;
// 一周的天数
private static final int DAYS_IN_ALL_WEEK = 7;
// 日期选择器的最小值
private static final int DATE_SPINNER_MIN_VAL = 0;
// 日期选择器的最大值
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
// 24 小时制下小时选择器的最小值
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
// 24 小时制下小时选择器的最大值
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
// 12 小时制下小时选择器的最小值
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
// 12 小时制下小时选择器的最大值
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
// 分钟选择器的最小值
private static final int MINUT_SPINNER_MIN_VAL = 0;
// 分钟选择器的最大值
private static final int MINUT_SPINNER_MAX_VAL = 59;
// AM/PM 选择器的最小值
private static final int AMPM_SPINNER_MIN_VAL = 0;
// AM/PM 选择器的最大值
private static final int AMPM_SPINNER_MAX_VAL = 1;
// 日期选择器
private final NumberPicker mDateSpinner;
// 小时选择器
private final NumberPicker mHourSpinner;
// 分钟选择器
private final NumberPicker mMinuteSpinner;
// AM/PM 选择器
private final NumberPicker mAmPmSpinner;
// 用于存储当前日期和时间的日历对象
private Calendar mDate;
// 用于显示日期的字符串数组
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
// 标记是否为上午
private boolean mIsAm;
// 标记是否为 24 小时制
private boolean mIs24HourView;
// 标记选择器是否启用
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
// 标记是否处于初始化状态
private boolean mInitialising;
// 日期和时间改变的监听器
private OnDateTimeChangedListener mOnDateTimeChangedListener;
/**
*
*
*/
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 根据新值和旧值的差值,更新日历对象的日期
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
// 更新日期选择器的显示
updateDateControl();
// 触发日期时间改变事件
onDateTimeChanged();
}
};
/**
*
*
*/
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 标记日期是否改变
boolean isDateChanged = false;
// 创建一个新的日历对象
Calendar cal = Calendar.getInstance();
if (!mIs24HourView) {
// 12 小时制下的处理
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
// 从下午 11 点到上午 12 点,日期加一天
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
// 从上午 12 点到下午 11 点,日期减一天
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
// 切换 AM/PM
mIsAm = !mIsAm;
// 更新 AM/PM 选择器的显示
updateAmPmControl();
}
} else {
// 24 小时制下的处理
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
// 从 23 点到 0 点,日期加一天
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
// 从 0 点到 23 点,日期减一天
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
}
// 计算新的小时数
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
// 更新日历对象的小时数
mDate.set(Calendar.HOUR_OF_DAY, newHour);
// 触发日期时间改变事件
onDateTimeChanged();
if (isDateChanged) {
// 如果日期改变,更新年、月、日
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}
}
};
/**
*
*
*/
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 获取分钟选择器的最小值
int minValue = mMinuteSpinner.getMinValue();
// 获取分钟选择器的最大值
int maxValue = mMinuteSpinner.getMaxValue();
// 偏移量,用于处理分钟跨小时的情况
int offset = 0;
if (oldVal == maxValue && newVal == minValue) {
// 从 59 分跳到 0 分,小时加 1
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
// 从 0 分跳到 59 分,小时减 1
offset -= 1;
}
if (offset != 0) {
// 如果有偏移量,更新日历对象的小时数
mDate.add(Calendar.HOUR_OF_DAY, offset);
// 更新小时选择器的显示
mHourSpinner.setValue(getCurrentHour());
// 更新日期选择器的显示
updateDateControl();
// 获取当前的小时数
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY) {
// 如果小时数大于等于 12标记为下午
mIsAm = false;
// 更新 AM/PM 选择器的显示
updateAmPmControl();
} else {
// 否则标记为上午
mIsAm = true;
// 更新 AM/PM 选择器的显示
updateAmPmControl();
}
}
// 更新日历对象的分钟数
mDate.set(Calendar.MINUTE, newVal);
// 触发日期时间改变事件
onDateTimeChanged();
}
};
/**
* AM/PM
* AM/PM AM/PM
*/
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 切换 AM/PM 状态
mIsAm = !mIsAm;
if (mIsAm) {
// 如果切换到上午,小时数减 12
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {
// 如果切换到下午,小时数加 12
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}
// 更新 AM/PM 选择器的显示
updateAmPmControl();
// 触发日期时间改变事件
onDateTimeChanged();
}
};
/**
*
*
*/
public interface OnDateTimeChangedListener {
/**
*
* @param view
* @param year
* @param month
* @param dayOfMonth
* @param hourOfDay 24
* @param minute
*/
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
/**
* 使
* @param context
*/
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
/**
* 使
* @param context
* @param date
*/
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context));
}
/**
* 使
* @param context
* @param date
* @param is24HourView 24
*/
public DateTimePicker(Context context, long date, boolean is24HourView) {
// 调用父类的构造函数
super(context);
// 初始化日历对象
mDate = Calendar.getInstance();
// 标记为初始化状态
mInitialising = true;
// 根据当前小时数判断是否为上午
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
// 加载布局文件
inflate(context, R.layout.datetime_picker, this);
// 初始化日期选择器
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
// 初始化小时选择器
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
// 初始化分钟选择器
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
// 获取 AM/PM 字符串数组
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
// 初始化 AM/PM 选择器
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// 更新日期选择器的显示
updateDateControl();
// 更新小时选择器的显示
updateHourControl();
// 更新 AM/PM 选择器的显示
updateAmPmControl();
// 设置时间格式
set24HourView(is24HourView);
// 设置当前日期和时间
setCurrentDate(date);
// 设置启用状态
setEnabled(isEnabled());
// 标记初始化完成
mInitialising = false;
}
/**
*
* @param enabled
*/
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
return;
}
// 调用父类的方法设置启用状态
super.setEnabled(enabled);
// 设置日期选择器的启用状态
mDateSpinner.setEnabled(enabled);
// 设置分钟选择器的启用状态
mMinuteSpinner.setEnabled(enabled);
// 设置小时选择器的启用状态
mHourSpinner.setEnabled(enabled);
// 设置 AM/PM 选择器的启用状态
mAmPmSpinner.setEnabled(enabled);
// 更新启用状态标记
mIsEnabled = enabled;
}
/**
*
* @return
*/
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
*
* @return
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
*
* @param date
*/
public void setCurrentDate(long date) {
// 创建一个新的日历对象
Calendar cal = Calendar.getInstance();
// 设置日历对象的时间
cal.setTimeInMillis(date);
// 设置年、月、日、小时、分钟
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
}
/**
*
*
* @param year
* @param month
* @param dayOfMonth
* @param hourOfDay 24
* @param minute
*/
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
// 分别调用设置年、月、日、小时、分钟的方法
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
}
/**
*
*
* @return
*/
public int getCurrentYear() {
// 从 mDate 日历对象中获取年份
return mDate.get(Calendar.YEAR);
}
/**
*
*
* @param year
*/
public void setCurrentYear(int year) {
// 如果不是初始化阶段且设置的年份与当前年份相同,则不进行操作
if (!mInitialising && year == getCurrentYear()) {
return;
}
// 设置 mDate 日历对象的年份
mDate.set(Calendar.YEAR, year);
// 更新日期选择器的显示
updateDateControl();
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
* 0 - 11
*
* @return
*/
public int getCurrentMonth() {
// 从 mDate 日历对象中获取月份
return mDate.get(Calendar.MONTH);
}
/**
*
*
* @param month
*/
public void setCurrentMonth(int month) {
// 如果不是初始化阶段且设置的月份与当前月份相同,则不进行操作
if (!mInitialising && month == getCurrentMonth()) {
return;
}
// 设置 mDate 日历对象的月份
mDate.set(Calendar.MONTH, month);
// 更新日期选择器的显示
updateDateControl();
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
*
*
* @return
*/
public int getCurrentDay() {
// 从 mDate 日历对象中获取日期
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
*
*
* @param dayOfMonth
*/
public void setCurrentDay(int dayOfMonth) {
// 如果不是初始化阶段且设置的日期与当前日期相同,则不进行操作
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
// 设置 mDate 日历对象的日期
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
// 更新日期选择器的显示
updateDateControl();
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
* 24 0 - 23
*
* @return 24
*/
public int getCurrentHourOfDay() {
// 从 mDate 日历对象中获取小时24小时制
return mDate.get(Calendar.HOUR_OF_DAY);
}
/**
* 24
*
* @return
*/
private int getCurrentHour() {
if (mIs24HourView){
// 如果是 24 小时制,直接返回 24 小时制的小时数
return getCurrentHourOfDay();
} else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
// 如果小时数大于 12减去 12 得到 12 小时制的小时数
return hour - HOURS_IN_HALF_DAY;
} else {
// 如果小时数为 0返回 12否则返回原小时数
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
}
/**
* 24 0 - 23
*
* @param hourOfDay 24
*/
public void setCurrentHour(int hourOfDay) {
// 如果不是初始化阶段且设置的小时数与当前小时数相同,则不进行操作
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
// 设置 mDate 日历对象的小时数24小时制
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
// 如果小时数大于等于 12标记为下午
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
// 如果小时数大于 12减去 12 得到 12 小时制的小时数
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
// 如果小时数小于 12标记为上午
mIsAm = true;
if (hourOfDay == 0) {
// 如果小时数为 0设置为 12
hourOfDay = HOURS_IN_HALF_DAY;
}
}
// 更新 AM/PM 选择器的显示
updateAmPmControl();
}
// 设置小时选择器的值
mHourSpinner.setValue(hourOfDay);
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
*
*
* @return
*/
public int getCurrentMinute() {
// 从 mDate 日历对象中获取分钟
return mDate.get(Calendar.MINUTE);
}
/**
*
*
* @param minute
*/
public void setCurrentMinute(int minute) {
// 如果不是初始化阶段且设置的分钟数与当前分钟数相同,则不进行操作
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
// 设置分钟选择器的值
mMinuteSpinner.setValue(minute);
// 设置 mDate 日历对象的分钟数
mDate.set(Calendar.MINUTE, minute);
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
* 24
*
* @return 24 true false
*/
public boolean is24HourView () {
return mIs24HourView;
}
/**
* 24
*
* @param is24HourView true 24 false 12
*/
public void set24HourView(boolean is24HourView) {
// 如果设置的模式与当前模式相同,则不进行操作
if (mIs24HourView == is24HourView) {
return;
}
// 更新 24 小时制标记
mIs24HourView = is24HourView;
// 根据 24 小时制标记设置 AM/PM 选择器的可见性
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
// 获取当前小时数24小时制
int hour = getCurrentHourOfDay();
// 更新小时选择器的显示
updateHourControl();
// 设置当前小时数
setCurrentHour(hour);
// 更新 AM/PM 选择器的显示
updateAmPmControl();
}
/**
*
*/
private void updateDateControl() {
// 创建一个新的日历对象
Calendar cal = Calendar.getInstance();
// 设置日历对象的时间为 mDate 的时间
cal.setTimeInMillis(mDate.getTimeInMillis());
// 将日期向前移动(一周的一半 + 1
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
// 清空日期选择器的显示值
mDateSpinner.setDisplayedValues(null);
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
// 日期向后移动一天
cal.add(Calendar.DAY_OF_YEAR, 1);
// 格式化日期并存储到显示值数组中
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
}
// 设置日期选择器的显示值
mDateSpinner.setDisplayedValues(mDateDisplayValues);
// 设置日期选择器的当前值为一周的中间位置
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
// 使日期选择器重绘
mDateSpinner.invalidate();
}
/**
* AM/PM
*/
private void updateAmPmControl() {
if (mIs24HourView) {
// 如果是 24 小时制,隐藏 AM/PM 选择器
mAmPmSpinner.setVisibility(View.GONE);
} else {
// 根据是否为上午设置 AM/PM 选择器的值
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
// 显示 AM/PM 选择器
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
/**
*
*/
private void updateHourControl() {
if (mIs24HourView) {
// 如果是 24 小时制,设置小时选择器的范围为 0 - 23
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
// 如果是 12 小时制,设置小时选择器的范围为 1 - 12
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
/**
*
*
* @param callback null
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
// 更新日期时间改变的监听器
mOnDateTimeChangedListener = callback;
}
/**
*
*/
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
// 调用监听器的方法,传递当前的年、月、日、小时、分钟
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}

@ -0,0 +1,117 @@
/*
* 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 java.util.Calendar;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
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;
// 该类继承自AlertDialog用于显示日期和时间选择对话框
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
// 存储选择的日期和时间
private Calendar mDate = Calendar.getInstance();
// 标记是否为24小时制显示
private boolean mIs24HourView;
// 日期和时间选择完成后的回调接口
private OnDateTimeSetListener mOnDateTimeSetListener;
// 自定义的日期和时间选择器
private DateTimePicker mDateTimePicker;
// 日期和时间选择完成后的回调接口
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
// 构造函数,传入上下文和初始日期
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) {
// 根据选择的日期和时间更新Calendar对象
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());
}
});
// 设置Calendar对象的时间为传入的初始日期
mDate.setTimeInMillis(date);
// 将秒数设置为0
mDate.set(Calendar.SECOND, 0);
// 设置日期和时间选择器的当前日期
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
// 设置对话框的确定按钮点击后触发onClick方法
setButton(context.getString(R.string.datetime_dialog_ok), this);
// 设置对话框的取消按钮,点击后不做处理
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
// 根据系统设置判断是否为24小时制显示
set24HourView(DateFormat.is24HourFormat(this.getContext()));
// 更新对话框的标题显示选择的日期和时间
updateTitle(mDate.getTimeInMillis());
}
// 设置是否为24小时制显示
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
// 设置日期和时间选择完成后的回调接口
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;
// 根据是否为24小时制显示添加相应的标志位
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());
}
}
}

@ -0,0 +1,317 @@
/*
* MiCode Apache License 2.0
*/
package net.micode.notes.model;
// 导入 Android 系统中与内容提供器操作、上下文管理、URI 处理等相关的类
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;
// 导入 Java 集合框架中的 ArrayList 类
import java.util.ArrayList;
/**
* Note
*/
public class Note {
// 存储笔记需要更新的差异值
private ContentValues mNoteDiffValues;
// 管理笔记的文本和通话数据
private NoteData mNoteData;
// 日志标签,用于日志记录
private static final String TAG = "Note";
/**
* ID
* @param context
* @param folderId ID
* @return ID
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis();
// 设置笔记的创建时间、修改时间、类型、本地修改标志和父文件夹 ID
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);
long noteId = 0;
try {
// 从 URI 中提取笔记 ID
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 true false
*/
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
if (!isLocalModified()) {
return true;
}
// 更新笔记信息
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;
}
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";
/**
* ID
*/
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
}
/**
*
* @return true false
*/
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
/**
* IDID 0
* @param id ID
*/
void setTextDataId(long id) {
if(id <= 0) {
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
}
/**
* IDID 0
* @param id ID
*/
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());
}
/**
*
* @param context
* @param noteId ID
* @return 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);
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 | OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
}
}
return null;
}
}
}

@ -0,0 +1,416 @@
/*
* 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;
/**
* Note
*
*/
public class Note {
// 用于存储笔记基本信息的差异值,这些值会在更新笔记时使用
private ContentValues mNoteDiffValues;
// 用于管理笔记的文本数据和通话数据
private NoteData mNoteData;
// 日志标签,用于在日志中标识该类的相关信息
private static final String TAG = "Note";
/**
* ID
*
* @param context
* @param folderId ID
* @return ID
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// 创建一个ContentValues对象用于存储要插入到数据库中的笔记信息
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);
// 设置笔记所属文件夹的ID
values.put(NoteColumns.PARENT_ID, folderId);
// 插入新笔记到数据库并获取返回的URI
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
try {
// 从URI中提取笔记的ID
noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
// 记录获取笔记ID时的错误信息
Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0;
}
if (noteId == -1) {
// 如果笔记ID为-1抛出异常表示笔记ID错误
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) {
// 调用NoteData对象的方法设置文本数据值
mNoteData.setTextData(key, value);
}
/**
* ID
*
* @param id ID
*/
public void setTextDataId(long id) {
// 调用NoteData对象的方法设置文本数据ID
mNoteData.setTextDataId(id);
}
/**
* ID
*
* @return ID
*/
public long getTextDataId() {
return mNoteData.mTextDataId;
}
/**
* ID
*
* @param id ID
*/
public void setCallDataId(long id) {
// 调用NoteData对象的方法设置通话数据ID
mNoteData.setCallDataId(id);
}
/**
*
*
* @param key
* @param value
*/
public void setCallData(String key, String value) {
// 调用NoteData对象的方法设置通话数据值
mNoteData.setCallData(key, value);
}
/**
*
*
* @return truefalse
*/
public boolean isLocalModified() {
// 检查笔记差异值是否有数据,或者笔记数据是否有本地修改
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
/**
*
*
* @param context
* @param noteId ID
* @return truefalse
*/
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
// 如果笔记ID小于等于0抛出异常表示笔记ID错误
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
if (!isLocalModified()) {
// 如果笔记没有本地修改直接返回true
return true;
}
/**
* LOCAL_MODIFIEDMODIFIED_DATE
* 使
*/
if (context.getContentResolver().update(
// 构建要更新的笔记的URI
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)) {
// 如果笔记数据有本地修改并且将笔记数据推送到内容解析器失败返回false
return 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";
/**
* ContentValuesID0
*/
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
}
/**
*
*
* @return truefalse
*/
boolean isLocalModified() {
// 检查文本数据或通话数据的ContentValues对象是否有数据
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
/**
* ID
*
* @param id ID
*/
void setTextDataId(long id) {
if(id <= 0) {
// 如果文本数据ID小于等于0抛出异常表示ID错误
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
}
/**
* ID
*
* @param id ID
*/
void setCallDataId(long id) {
if (id <= 0) {
// 如果通话数据ID小于等于0抛出异常表示ID错误
throw new IllegalArgumentException("Call data id should larger than 0");
}
mCallDataId = id;
}
/**
*
*
* @param key
* @param value
*/
void setCallData(String key, String value) {
// 将键值对添加到通话数据的ContentValues对象中
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) {
// 将键值对添加到文本数据的ContentValues对象中
mTextDataValues.put(key, value);
// 标记笔记为本地修改过
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
// 更新笔记的修改时间为当前时间
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
*
* @param context
* @param noteId ID
* @return URInull
*/
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* ID0
*/
if (noteId <= 0) {
// 如果笔记ID小于等于0抛出异常表示笔记ID错误
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
// 创建一个ArrayList用于存储要执行的内容提供者操作
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 内容提供者操作的构建器
ContentProviderOperation.Builder builder = null;
if(mTextDataValues.size() > 0) {
// 设置文本数据所属的笔记ID
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
if (mTextDataId == 0) {
// 如果文本数据ID为0说明是新的文本数据
// 设置文本数据的MIME类型
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
// 插入新的文本数据到数据库并获取返回的URI
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
try {
// 从URI中提取文本数据的ID并设置到mTextDataId中
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 记录插入新文本数据失败的错误信息
Log.e(TAG, "Insert new text data fail with noteId" + noteId);
// 清空文本数据的ContentValues对象
mTextDataValues.clear();
return null;
}
} else {
// 如果文本数据ID不为0说明是更新已有的文本数据
// 创建一个更新操作的构建器
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
// 设置要更新的值
builder.withValues(mTextDataValues);
// 将更新操作添加到操作列表中
operationList.add(builder.build());
}
// 清空文本数据的ContentValues对象
mTextDataValues.clear();
}
if(mCallDataValues.size() > 0) {
// 设置通话数据所属的笔记ID
mCallDataValues.put(DataColumns.NOTE_ID, noteId);
if (mCallDataId == 0) {
// 如果通话数据ID为0说明是新的通话数据
// 设置通话数据的MIME类型
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
// 插入新的通话数据到数据库并获取返回的URI
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mCallDataValues);
try {
// 从URI中提取通话数据的ID并设置到mCallDataId中
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 记录插入新通话数据失败的错误信息
Log.e(TAG, "Insert new call data fail with noteId" + noteId);
// 清空通话数据的ContentValues对象
mCallDataValues.clear();
return null;
}
} else {
// 如果通话数据ID不为0说明是更新已有的通话数据
// 创建一个更新操作的构建器
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mCallDataId));
// 设置要更新的值
builder.withValues(mCallDataValues);
// 将更新操作添加到操作列表中
operationList.add(builder.build());
}
// 清空通话数据的ContentValues对象
mCallDataValues.clear();
}
if (operationList.size() > 0) {
try {
// 批量执行操作列表中的内容提供者操作
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
// 如果操作结果为空或长度为0或第一个结果为空返回null否则返回笔记的URI
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;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,331 @@
/*
* 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.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;
/**
* NoteEditText EditText
*
*/
public class NoteEditText extends EditText {
// 日志标签,用于调试日志输出
private static final String TAG = "NoteEditText";
// 当前编辑文本框的索引,用于标识其在列表中的位置
private int mIndex;
// 删除操作前的光标起始位置
private int mSelectionStartBeforeDelete;
// 定义电话链接的协议前缀
private static final String SCHEME_TEL = "tel:" ;
// 定义 HTTP 链接的协议前缀
private static final String SCHEME_HTTP = "http:" ;
// 定义邮件链接的协议前缀
private static final String SCHEME_EMAIL = "mailto:" ;
// 存储协议前缀与对应的菜单项资源 ID 的映射
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
// 初始化电话链接对应的菜单项资源 ID
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
// 初始化 HTTP 链接对应的菜单项资源 ID
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
// 初始化邮件链接对应的菜单项资源 ID
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
* OnTextViewChangeListener
*
*/
public interface OnTextViewChangeListener {
/**
* {@link KeyEvent#KEYCODE_DEL}
*
*
* @param index
* @param text
*/
void onEditTextDelete(int index, String text);
/**
* {@link KeyEvent#KEYCODE_ENTER}
*
*
* @param index
* @param text
*/
void onEditTextEnter(int index, String text);
/**
*
*
* @param index
* @param hasText
*/
void onTextChange(int index, boolean hasText);
}
// 文本变化监听器实例
private OnTextViewChangeListener mOnTextViewChangeListener;
/**
* 使 NoteEditText
*
* @param context
*/
public NoteEditText(Context context) {
super(context, null);
// 初始化索引为 0
mIndex = 0;
}
/**
*
*
* @param index
*/
public void setIndex(int index) {
mIndex = index;
}
/**
*
*
* @param listener OnTextViewChangeListener
*/
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
/**
* 使 NoteEditText
*
* @param context
* @param attrs
*/
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
/**
* 使 NoteEditText
*
* @param context
* @param attrs
* @param defStyle
*/
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
/**
*
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取触摸点的 x 坐标
int x = (int) event.getX();
// 获取触摸点的 y 坐标
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);
}
/**
*
*
* @param keyCode
* @param event
* @return
*/
@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);
}
/**
*
*
* @param keyCode
* @param event
* @return
*/
@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);
}
/**
*
*
* @param focused
* @param direction
* @param previouslyFocusedRect
*/
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
// 失去焦点且文本为空时,调用文本变化监听器方法
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
// 其他情况,调用文本变化监听器方法
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
/**
*
*
* @param menu
*/
@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);
// 获取选中区域内的所有 URLSpan 对象
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) {
// 根据链接协议获取对应的菜单项资源 ID
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
// 若未匹配到协议,使用默认的菜单项资源 ID
defaultResId = R.string.note_link_other;
}
// 添加菜单项并设置点击监听器
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// 点击菜单项时,调用 URLSpan 的点击方法
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu);
}
}

@ -0,0 +1,125 @@
/*
* Apache 2.0
*/
// 声明类所在的包
package net.micode.notes.data;
// 导入 Android 中的 Uri 类,用于处理统一资源标识符
import android.net.Uri;
/**
* Notes URI
*/
public class Notes {
// 内容提供者的权限,用于标识应用的内容提供者
public static final String AUTHORITY = "micode_notes";
// 日志标签,方便调试时定位与 Notes 类相关的信息
public static final String TAG = "Notes";
// 笔记类型常量
public static final int TYPE_NOTE = 0;
public static final int TYPE_FOLDER = 1;
public static final int TYPE_SYSTEM = 2;
// 系统文件夹 ID 常量
public static final int ID_ROOT_FOLDER = 0;
public static final int ID_TEMPARAY_FOLDER = -1;
public static final int ID_CALL_RECORD_FOLDER = -2;
public static final int ID_TRASH_FOLER = -3;
// 意图额外数据的键名常量
public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date";
public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id";
public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id";
public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type";
public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id";
public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";
// 小部件类型常量
public static final int TYPE_WIDGET_INVALIDE = -1;
public static final int TYPE_WIDGET_2X = 0;
public static final int TYPE_WIDGET_4X = 1;
/**
* DataConstants
*/
public static class DataConstants {
// 文本笔记的内容项类型
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
// 通话笔记的内容项类型
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;
}
// 查询所有笔记和文件夹的 URI
public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");
// 查询数据的 URI
public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
/**
* NoteColumns
*/
public interface NoteColumns {
public static final String ID = "_id";
public static final String PARENT_ID = "parent_id";
public static final String CREATED_DATE = "created_date";
public static final String MODIFIED_DATE = "modified_date";
public static final String ALERTED_DATE = "alert_date";
public static final String SNIPPET = "snippet";
public static final String WIDGET_ID = "widget_id";
public static final String WIDGET_TYPE = "widget_type";
public static final String BG_COLOR_ID = "bg_color_id";
public static final String HAS_ATTACHMENT = "has_attachment";
public static final String NOTES_COUNT = "notes_count";
public static final String TYPE = "type";
public static final String SYNC_ID = "sync_id";
public static final String LOCAL_MODIFIED = "local_modified";
public static final String ORIGIN_PARENT_ID = "origin_parent_id";
public static final String GTASK_ID = "gtask_id";
public static final String VERSION = "version";
}
/**
* DataColumns
*/
public interface DataColumns {
public static final String ID = "_id";
public static final String MIME_TYPE = "mime_type";
public static final String NOTE_ID = "note_id";
public static final String CREATED_DATE = "created_date";
public static final String MODIFIED_DATE = "modified_date";
public static final String CONTENT = "content";
public static final String DATA1 = "data1";
public static final String DATA2 = "data2";
public static final String DATA3 = "data3";
public static final String DATA4 = "data4";
public static final String DATA5 = "data5";
}
/**
* TextNote URI
*/
public static final class TextNote implements DataColumns {
// 文本笔记模式,用于表示是否为检查列表模式
public static final String MODE = DATA1;
public static final int MODE_CHECK_LIST = 1;
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");
}
/**
* CallNote URI
*/
public static final class CallNote implements DataColumns {
// 通话日期
public static final String CALL_DATE = DATA1;
// 电话号码
public static final String PHONE_NUMBER = DATA3;
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");
}
}

@ -0,0 +1,283 @@
/*
* 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.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
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.data.NotesDatabaseHelper.TABLE;
import net.micode.notes.gtask.exception.ActionFailureException;
import org.json.JSONException;
import org.json.JSONObject;
/**
* SqlData
*
*/
public class SqlData {
// 日志标签,用于标识该类的日志信息
private static final String TAG = SqlData.class.getSimpleName();
// 无效的 ID 值,用于初始化或标识无效的 ID
private static final int INVALID_ID = -99999;
// 查询数据时使用的投影,指定要查询的列
public static final String[] PROJECTION_DATA = new String[] {
DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1,
DataColumns.DATA3
};
// 数据 ID 列在投影数组中的索引
public static final int DATA_ID_COLUMN = 0;
// 数据 MIME 类型列在投影数组中的索引
public static final int DATA_MIME_TYPE_COLUMN = 1;
// 数据内容列在投影数组中的索引
public static final int DATA_CONTENT_COLUMN = 2;
// 数据内容的 DATA1 列在投影数组中的索引
public static final int DATA_CONTENT_DATA_1_COLUMN = 3;
// 数据内容的 DATA3 列在投影数组中的索引
public static final int DATA_CONTENT_DATA_3_COLUMN = 4;
// 内容解析器,用于与内容提供者进行交互
private ContentResolver mContentResolver;
// 标记数据是否为新创建的
private boolean mIsCreate;
// 数据的 ID
private long mDataId;
// 数据的 MIME 类型
private String mDataMimeType;
// 数据的内容
private String mDataContent;
// 数据内容的 DATA1 字段
private long mDataContentData1;
// 数据内容的 DATA3 字段
private String mDataContentData3;
// 存储数据差异的 ContentValues 对象,用于更新或插入数据
private ContentValues mDiffDataValues;
/**
* SqlData
*
* @param context
*/
public SqlData(Context context) {
// 获取内容解析器
mContentResolver = context.getContentResolver();
// 标记为新创建的数据
mIsCreate = true;
// 初始化数据 ID 为无效值
mDataId = INVALID_ID;
// 初始化数据 MIME 类型为默认值
mDataMimeType = DataConstants.NOTE;
// 初始化数据内容为空字符串
mDataContent = "";
// 初始化数据内容的 DATA1 字段为 0
mDataContentData1 = 0;
// 初始化数据内容的 DATA3 字段为空字符串
mDataContentData3 = "";
// 初始化差异数据的 ContentValues 对象
mDiffDataValues = new ContentValues();
}
/**
* SqlData
*
* @param context
* @param c
*/
public SqlData(Context context, Cursor c) {
// 获取内容解析器
mContentResolver = context.getContentResolver();
// 标记为非新创建的数据
mIsCreate = false;
// 从游标中加载数据
loadFromCursor(c);
// 初始化差异数据的 ContentValues 对象
mDiffDataValues = new ContentValues();
}
/**
*
*
* @param c
*/
private void loadFromCursor(Cursor c) {
// 从游标中获取数据 ID
mDataId = c.getLong(DATA_ID_COLUMN);
// 从游标中获取数据 MIME 类型
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
// 从游标中获取数据内容
mDataContent = c.getString(DATA_CONTENT_COLUMN);
// 从游标中获取数据内容的 DATA1 字段
mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);
// 从游标中获取数据内容的 DATA3 字段
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
}
/**
* JSON
*
* @param js JSON
* @throws JSONException JSON
*/
public void setContent(JSONObject js) throws JSONException {
// 从 JSON 对象中获取数据 ID如果不存在则使用无效值
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
// 如果是新创建的数据或者数据 ID 不同,则记录差异
if (mIsCreate || mDataId != dataId) {
mDiffDataValues.put(DataColumns.ID, dataId);
}
// 更新数据 ID
mDataId = dataId;
// 从 JSON 对象中获取数据 MIME 类型,如果不存在则使用默认值
String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE)
: DataConstants.NOTE;
// 如果是新创建的数据或者 MIME 类型不同,则记录差异
if (mIsCreate || !mDataMimeType.equals(dataMimeType)) {
mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType);
}
// 更新数据 MIME 类型
mDataMimeType = dataMimeType;
// 从 JSON 对象中获取数据内容,如果不存在则使用空字符串
String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : "";
// 如果是新创建的数据或者数据内容不同,则记录差异
if (mIsCreate || !mDataContent.equals(dataContent)) {
mDiffDataValues.put(DataColumns.CONTENT, dataContent);
}
// 更新数据内容
mDataContent = dataContent;
// 从 JSON 对象中获取数据内容的 DATA1 字段,如果不存在则使用 0
long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;
// 如果是新创建的数据或者 DATA1 字段不同,则记录差异
if (mIsCreate || mDataContentData1 != dataContentData1) {
mDiffDataValues.put(DataColumns.DATA1, dataContentData1);
}
// 更新数据内容的 DATA1 字段
mDataContentData1 = dataContentData1;
// 从 JSON 对象中获取数据内容的 DATA3 字段,如果不存在则使用空字符串
String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";
// 如果是新创建的数据或者 DATA3 字段不同,则记录差异
if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {
mDiffDataValues.put(DataColumns.DATA3, dataContentData3);
}
// 更新数据内容的 DATA3 字段
mDataContentData3 = dataContentData3;
}
/**
* JSON
*
* @return JSON
* @throws JSONException JSON
*/
public JSONObject getContent() throws JSONException {
// 如果是新创建的数据且未提交到数据库,则记录错误日志并返回 null
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
// 创建一个新的 JSON 对象
JSONObject js = new JSONObject();
// 将数据 ID 放入 JSON 对象
js.put(DataColumns.ID, mDataId);
// 将数据 MIME 类型放入 JSON 对象
js.put(DataColumns.MIME_TYPE, mDataMimeType);
// 将数据内容放入 JSON 对象
js.put(DataColumns.CONTENT, mDataContent);
// 将数据内容的 DATA1 字段放入 JSON 对象
js.put(DataColumns.DATA1, mDataContentData1);
// 将数据内容的 DATA3 字段放入 JSON 对象
js.put(DataColumns.DATA3, mDataContentData3);
return js;
}
/**
*
*
* @param noteId ID
* @param validateVersion
* @param version
*/
public void commit(long noteId, boolean validateVersion, long version) {
if (mIsCreate) {
// 如果数据 ID 无效且差异数据中包含 ID 字段,则移除该字段
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
mDiffDataValues.remove(DataColumns.ID);
}
// 将笔记 ID 放入差异数据中
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
// 插入数据到数据库
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
try {
// 从插入结果的 URI 中获取数据 ID
mDataId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
// 记录错误日志并抛出异常
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
} else {
// 如果有差异数据需要更新
if (mDiffDataValues.size() > 0) {
int result = 0;
if (!validateVersion) {
// 不验证版本时,直接更新数据
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);
} else {
// 验证版本时,根据版本号进行更新
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues,
" ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.VERSION + "=?)", new String[] {
String.valueOf(noteId), String.valueOf(version)
});
}
// 如果更新结果为 0记录警告日志
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
}
// 清空差异数据
mDiffDataValues.clear();
// 标记数据为非新创建
mIsCreate = false;
}
/**
* ID
*
* @return ID
*/
public long getId() {
return mDataId;
}
}

@ -0,0 +1,387 @@
/*
* MiCode 2010 - 2011 Apache License 2.0
*/
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;
// WorkingNote 类用于管理笔记的创建、加载、保存和设置更改等操作
public class WorkingNote {
// 笔记对象,用于存储笔记的详细信息
private Note mNote;
// 笔记的唯一标识符
private long mNoteId;
// 笔记的内容
private String mContent;
// 笔记的模式,例如清单模式等
private int mMode;
// 笔记的提醒日期
private long mAlertDate;
// 笔记的最后修改日期
private long mModifiedDate;
// 笔记的背景颜色 ID
private int mBgColorId;
// 笔记关联的小部件 ID
private int mWidgetId;
// 笔记关联的小部件类型
private int mWidgetType;
// 笔记所属的文件夹 ID
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
};
// 数据查询结果中 ID 列的索引
private static final int DATA_ID_COLUMN = 0;
// 数据查询结果中内容列的索引
private static final int DATA_CONTENT_COLUMN = 1;
// 数据查询结果中 MIME 类型列的索引
private static final int DATA_MIME_TYPE_COLUMN = 2;
// 数据查询结果中模式列的索引
private static final int DATA_MODE_COLUMN = 3;
// 笔记查询结果中父文件夹 ID 列的索引
private static final int NOTE_PARENT_ID_COLUMN = 0;
// 笔记查询结果中提醒日期列的索引
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
// 笔记查询结果中背景颜色 ID 列的索引
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
// 笔记查询结果中小部件 ID 列的索引
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;
// 构造函数,用于创建新笔记
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0; // 初始提醒日期为 0
mModifiedDate = System.currentTimeMillis(); // 设置最后修改日期为当前时间
mFolderId = folderId;
mNote = new Note();
mNoteId = 0; // 新笔记 ID 初始为 0
mIsDeleted = false;
mMode = 0;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE; // 初始小部件类型为无效
}
// 构造函数,用于加载已存在的笔记
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 {
// 获取数据的 MIME 类型
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)) {
// 如果是通话笔记,设置通话数据 ID
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); // 设置小部件 ID
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) {
// 创建新笔记失败,记录错误日志并返回 false
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
mNote.syncNote(mContext, mNoteId); // 同步笔记信息到数据库
// 如果笔记关联了有效的小部件,且设置了监听器,则通知小部件更新
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();
}
}
// 设置笔记背景颜色 ID
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));
}
}
// 设置笔记关联小部件 ID
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;
}
// 获取笔记背景资源 ID
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
// 获取笔记背景颜色 ID
public int getBgColorId() {
return mBgColorId;
}
// 获取笔记标题背景资源 ID
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
// 获取笔记清单模式
public int getCheckListMode() {
return mMode;
}
// 获取笔记 ID
public long getNoteId() {
return mNoteId;
}
// 获取笔记所属文件夹 ID
public long getFolderId() {
return mFolderId;
}
// 获取笔记关联小部件 ID
public int getWidgetId() {
return mWidgetId;
}
// 获取笔记关联小部件类型
public int getWidgetType() {
return mWidgetType;
}
// 笔记设置变化监听器接口
public interface NoteSettingChangedListener {
// 背景颜色变化时调用
void onBackgroundColorChanged();
// 用户设置提醒时调用
void onClockAlertChanged(long date, boolean set);
// 用户从小部件创建笔记时调用
void onWidgetChanged();
// 清单模式切换时调用
void onCheckListModeChanged(int oldMode, int newMode);
}
}

@ -0,0 +1,468 @@
/*
* 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;
/**
* WorkingNote
*
*/
public class WorkingNote {
// 笔记对象,用于存储笔记的相关数据
private Note mNote;
// 笔记的ID
private long mNoteId;
// 笔记的内容
private String mContent;
// 笔记的模式,例如普通模式或清单模式
private int mMode;
// 笔记的提醒日期
private long mAlertDate;
// 笔记的修改日期
private long mModifiedDate;
// 笔记的背景颜色ID
private int mBgColorId;
// 笔记关联的小部件ID
private int mWidgetId;
// 笔记关联的小部件类型
private int mWidgetType;
// 笔记所属的文件夹ID
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
};
// DATA_PROJECTION中ID列的索引
private static final int DATA_ID_COLUMN = 0;
// DATA_PROJECTION中内容列的索引
private static final int DATA_CONTENT_COLUMN = 1;
// DATA_PROJECTION中MIME类型列的索引
private static final int DATA_MIME_TYPE_COLUMN = 2;
// DATA_PROJECTION中模式列的索引
private static final int DATA_MODE_COLUMN = 3;
// NOTE_PROJECTION中父ID列的索引
private static final int NOTE_PARENT_ID_COLUMN = 0;
// NOTE_PROJECTION中提醒日期列的索引
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
// NOTE_PROJECTION中背景颜色ID列的索引
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
// NOTE_PROJECTION中小部件ID列的索引
private static final int NOTE_WIDGET_ID_COLUMN = 3;
// NOTE_PROJECTION中小部件类型列的索引
private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
// NOTE_PROJECTION中修改日期列的索引
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
/**
*
*
* @param context
* @param folderId ID
*/
private WorkingNote(Context context, long folderId) {
mContext = context;
// 初始提醒日期为0表示没有提醒
mAlertDate = 0;
// 修改日期设置为当前时间
mModifiedDate = System.currentTimeMillis();
mFolderId = folderId;
mNote = new Note();
// 新笔记的ID初始为0
mNoteId = 0;
// 标记笔记未被删除
mIsDeleted = false;
// 初始模式为0
mMode = 0;
// 初始小部件类型为无效类型
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
/**
*
*
* @param context
* @param noteId ID
* @param folderId ID
*/
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
// 加载笔记的相关信息
loadNote();
}
/**
*
*/
private void loadNote() {
// 查询指定ID的笔记信息
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
// 获取笔记所属的文件夹ID
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
// 获取笔记的背景颜色ID
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
// 获取笔记关联的小部件ID
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() {
// 查询指定笔记ID的数据信息
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 {
// 获取数据的MIME类型
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);
// 设置文本数据的ID
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
// 如果是通话笔记类型设置通话数据的ID
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);
}
}
/**
*
*
* @param context
* @param folderId ID
* @param widgetId ID
* @param widgetType
* @param defaultBgColorId ID
* @return
*/
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
// 设置笔记的背景颜色ID
note.setBgColorId(defaultBgColorId);
// 设置笔记关联的小部件ID
note.setWidgetId(widgetId);
// 设置笔记关联的小部件类型
note.setWidgetType(widgetType);
return note;
}
/**
* ID
*
* @param context
* @param id ID
* @return
*/
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
/**
*
*
* @return truefalse
*/
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
// 如果笔记不存在于数据库中创建新的笔记ID
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
// 若创建失败记录错误日志并返回false
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
// 同步笔记数据到数据库
mNote.syncNote(mContext, mNoteId);
/**
*
*
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
}
/**
*
*
* @return truefalse
*/
public boolean existInDatabase() {
return mNoteId > 0;
}
/**
*
*
* @return truefalse
*/
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
/**
*
*
* @param l
*/
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
/**
*
*
* @param date
* @param set
*/
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);
}
}
/**
*
*
* @param mark
*/
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
// 通知监听器小部件内容已更改
mNoteSettingStatusListener.onWidgetChanged();
}
}
/**
* ID
*
* @param id ID
*/
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
// 通知监听器背景颜色已更改
mNoteSettingStatusListener.onBackgroundColorChanged();
}
// 设置笔记的背景颜色ID值
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
/**
*
*
* @param mode
*/
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
// 通知监听器清单模式已更改
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
// 设置笔记的文本数据模式值
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
/**
*
*
* @param type
*/
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
// 设置笔记的小部件类型值
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
}
/**
* ID
*
* @param id ID
*/
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
// 设置笔记的小部件ID值
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
}
/**
*
*
* @param text
*/
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
// 设置笔记的文本数据内容值
mNote.setTextData(DataColumns.CONTENT, mContent);
}
}
/**
*
*
* @param phoneNumber
* @param callDate
*/
public void convertToCallNote(String phoneNumber, long callDate) {
// 设置通话笔记的通话日期
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
// 设置通话笔记的电话号码
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
// 设置笔记的父ID为通话记录文件夹ID
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
}
/**
*
*
* @return truefalse
*/
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
/**
*
*
* @return
*/
public String getContent() {
return mContent;
}
/**
*
*
* @return
*/
public long getAlertDate() {
return mAlertDate;
}
/**
Loading…
Cancel
Save