最终合并 #24

Merged
pevl7hxk5 merged 28 commits from a_branch into develop 9 months ago

@ -5,6 +5,9 @@
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="MainActivity">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">

@ -24,12 +24,13 @@ import net.micode.notes.tool.GTaskStringUtils;
import org.json.JSONException;
import org.json.JSONObject;
// 该类继承自Task类用于处理与Google任务相关的元数据
public class MetaData extends Task {
private final static String TAG = MetaData.class.getSimpleName();
private String mRelatedGid = null;
// 设置元数据信息包括Google任务ID和元数据信息
public void setMeta(String gid, JSONObject metaInfo) {
try {
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
@ -40,15 +41,18 @@ public class MetaData extends Task {
setName(GTaskStringUtils.META_NOTE_NAME);
}
// 获取相关的Google任务ID
public String getRelatedGid() {
return mRelatedGid;
}
// 判断该元数据是否值得保存
@Override
public boolean isWorthSaving() {
return getNotes() != null;
}
// 通过远程JSON数据设置内容
@Override
public void setContentByRemoteJSON(JSONObject js) {
super.setContentByRemoteJSON(js);
@ -63,17 +67,20 @@ public class MetaData extends Task {
}
}
// 该方法不应用于MetaData类调用时会抛出异常
@Override
public void setContentByLocalJSON(JSONObject js) {
// this function should not be called
throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");
}
// 该方法不应用于MetaData类调用时会抛出异常
@Override
public JSONObject getLocalJSONFromContent() {
throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");
}
// 该方法不应用于MetaData类调用时会抛出异常
@Override
public int getSyncAction(Cursor c) {
throw new IllegalAccessError("MetaData:getSyncAction should not be called");

@ -41,98 +41,133 @@ import java.io.IOException;
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 mPlayer;
@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的Data中解析出便签ID
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 通过ContentResolver获取便签的摘要内容
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) {
// 处理参数异常打印堆栈信息并结束Activity
e.printStackTrace();
return;
}
// 创建媒体播放器实例
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);
// 根据静默模式设置音频流类型
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置音频数据源
mPlayer.setDataSource(this, url);
// 准备播放器
mPlayer.prepare();
// 设置循环播放
mPlayer.setLooping(true);
// 开始播放
mPlayer.start();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
// 参数异常处理
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
// 安全异常处理
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
// 状态异常处理
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
// IO异常处理
e.printStackTrace();
}
}
// 显示提醒操作对话框
private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框标题为应用名称
dialog.setTitle(R.string.app_name);
// 设置对话框内容为便签摘要
dialog.setMessage(mSnippet);
// 添加"确定"按钮,并设置点击监听器
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕已开启,添加"进入"按钮
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 显示对话框并设置消失监听器
dialog.show().setOnDismissListener(this);
}
// 对话框按钮点击事件处理
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
// 点击"进入"按钮启动便签编辑Activity查看便签详情
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
@ -143,15 +178,22 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
// 对话框消失时的回调处理
public void onDismiss(DialogInterface dialog) {
// 停止闹钟声音播放
stopAlarmSound();
// 结束当前Activity
finish();
}
// 停止闹钟声音并释放资源
private void stopAlarmSound() {
if (mPlayer != null) {
// 停止播放
mPlayer.stop();
// 释放媒体播放器资源
mPlayer.release();
// 将引用置为null便于垃圾回收
mPlayer = null;
}
}

@ -28,38 +28,65 @@ import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
/**
* 广便
*/
public class AlarmInitReceiver extends BroadcastReceiver {
// 查询闹钟提醒相关便签数据时使用的列投影
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
NoteColumns.ID, // 便签ID
NoteColumns.ALERTED_DATE // 提醒日期时间
};
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
// 投影列索引常量,便于代码引用
private static final int COLUMN_ID = 0; // ID列索引
private static final int COLUMN_ALERTED_DATE = 1; // 提醒日期列索引
/**
* 广
* @param context
* @param intent Intent
*/
@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);
new String[] { String.valueOf(currentDate) }, // 查询参数
null); // 不指定排序
// 处理查询结果
if (c != null) {
if (c.moveToFirst()) {
// 遍历所有符合条件的便签
do {
// 获取便签的提醒时间
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个将在提醒时间触发的广播Intent
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);
// 设置一个精确的闹钟,当到达提醒时间时唤醒设备并触发广播
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
// 关闭Cursor释放资源
c.close();
}
}
}
}

@ -227,6 +227,17 @@ public class DateTimePicker extends FrameLayout {
mIsEnabled = enabled;
}
@Override
public boolean isEnabled() {
return mIsEnabled;

@ -31,60 +31,113 @@ import android.text.format.DateUtils;
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 {
/**
*
* @param dialog
* @param date
*/
void OnDateTimeSet(AlertDialog dialog, long date);
}
/**
*
* @param context
* @param 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) {
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());
}
});
// 设置初始日期时间并将秒数设为0
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);
// 根据系统设置确定是否使用24小时制
set24HourView(DateFormat.is24HourFormat(this.getContext()));
// 更新对话框标题
updateTitle(mDate.getTimeInMillis());
}
/**
* 使24
* @param is24HourView true使24false使12
*/
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
/**
*
* @param callBack OnDateTimeSetListener
*/
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
/**
*
* @param date
*/
private void updateTitle(long date) {
// 设置日期时间显示格式标志
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
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));
}
/**
*
* @param arg0
* @param arg1
*/
public void onClick(DialogInterface arg0, int arg1) {
// 当点击"确定"按钮时,调用回调函数通知日期时间已设置
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
}
}
}

@ -33,11 +33,16 @@ public class DropdownMenu {
private Menu mMenu;
public DropdownMenu(Context context, Button button, int menuId) {
// 保存按钮引用并设置下拉图标背景
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
// 创建PopupMenu实例并加载菜单资源
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
// 设置按钮点击事件,点击时显示菜单
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();

@ -29,52 +29,97 @@ import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
/**
* ListView
*/
public class FoldersListAdapter extends CursorAdapter {
// 查询文件夹数据时使用的列投影,指定需要查询的数据库列
public static final String [] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
NoteColumns.ID, // 文件夹ID
NoteColumns.SNIPPET // 文件夹名称(摘要)
};
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
// 列索引常量,方便后续代码引用
public static final int ID_COLUMN = 0; // ID列的索引
public static final int NAME_COLUMN = 1; // 名称列的索引
/**
*
* @param context
* @param c Cursor
*/
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
}
/**
*
* @param context
* @param cursor
* @param parent
* @return
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// 创建自定义的文件夹列表项视图
return new FolderListItem(context);
}
/**
*
* @param view
* @param context
* @param cursor
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) {
// 如果是根文件夹,则显示"上级文件夹"文本,否则显示实际文件夹名称
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
// 将文件夹名称绑定到视图上
((FolderListItem) view).bind(folderName);
}
}
/**
*
* @param context
* @param position
* @return
*/
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position);
// 如果是根文件夹,则显示"上级文件夹"文本,否则显示实际文件夹名称
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
/**
*
*/
private class FolderListItem extends LinearLayout {
private TextView mName;
private TextView mName; // 文件夹名称显示文本视图
/**
*
* @param context
*/
public FolderListItem(Context context) {
super(context);
// 加载布局文件
inflate(context, R.layout.folder_list_item, this);
// 获取文件夹名称文本视图引用
mName = (TextView) findViewById(R.id.tv_folder_name);
}
/**
*
* @param name
*/
public void bind(String name) {
mName.setText(name);
}
}
}
}

@ -430,7 +430,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (id == R.id.btn_set_bg_color) {
mNoteBgColorSelector.setVisibility(View.VISIBLE);
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
- View.VISIBLE);
View.VISIBLE);
} else if (sBgSelectorBtnsMap.containsKey(id)) {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE);
@ -623,7 +623,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (mWorkingNote.getNoteId() > 0) {
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
showAlertHeader();
if(!set) {

@ -49,6 +49,7 @@ public class NotesListItem extends LinearLayout {
}
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
// 根据选择模式和笔记类型决定是否显示复选框
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
@ -56,36 +57,49 @@ public class NotesListItem extends LinearLayout {
mCheckBox.setVisibility(View.GONE);
}
// 保存笔记数据引用
mItemData = data;
// 处理通话记录文件夹
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
// 设置文件夹名称和包含的文件数量
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
}
// 处理通话记录子项
else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
// 根据是否有提醒设置提醒图标
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
} else {
}
// 处理普通笔记和文件夹
else {
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
// 处理文件夹
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
}
// 处理普通笔记
else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
// 根据是否有提醒设置提醒图标
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
@ -94,11 +108,13 @@ public class NotesListItem extends LinearLayout {
}
}
}
// 设置修改时间,使用相对时间格式(如"1小时前"
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
// 根据笔记状态设置背景
setBackground(data);
}
private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
if (data.getType() == Notes.TYPE_NOTE) {

@ -56,7 +56,7 @@
android:layout_marginRight="8dip"
android:textAppearance="@style/TextAppearanceSecondaryItem" />
<ImageButton
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"

@ -63,7 +63,6 @@
</style>
<style name="NoteActionBarStyle" parent="@android:style/Widget.Holo.Light.ActionBar.Solid">
<item name="android:displayOptions" />
<item name="android:visibility">gone</item>
<item name="android:visibility">visible</item>
</style>
</resources>
Loading…
Cancel
Save