窦志伟 3 months ago
parent b60609da6e
commit 989ff5d6d9

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Before

Width:  |  Height:  |  Size: 251 KiB

After

Width:  |  Height:  |  Size: 251 KiB

@ -0,0 +1,450 @@
/*
* 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.text.DateFormatSymbols;
import java.util.Calendar;
import net.micode.notes.R;
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;
public class DateTimePicker extends FrameLayout {
// 默认启用状态为 true
private static final boolean DEFAULT_ENABLE_STATE = true;
// 一天中的小时数12小时制
private static final int HOURS_IN_HALF_DAY = 12;
// 一天中的小时数24小时制
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;
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;
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;
private static final int AMPM_SPINNER_MAX_VAL = 1;
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
private Calendar mDate;
// 用于显示日期的字符串数组
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
// 标记是否是AM
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小时制处理AM/PM转换
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
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) {
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) {
mIsAm = !mIsAm; // 切换AM/PM
updateAmPmControl(); // 更新AM/PM控件
}
} else {
// 如果是24小时制处理跨日的变化
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
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) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
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) {
mIsAm = false;
updateAmPmControl(); // 更新AM/PM控件
} else {
mIsAm = true;
updateAmPmControl(); // 更新AM/PM控件
}
}
mDate.set(Calendar.MINUTE, newVal); // 设置分钟
onDateTimeChanged(); // 触发日期时间变化事件
}
};
// AM/PM选择变化监听器
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm; // 切换AM/PM
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); // 如果是AM减少12小时
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); // 如果是PM增加12小时
}
updateAmPmControl(); // 更新AM/PM控件
onDateTimeChanged(); // 触发日期时间变化事件
}
};
// 日期时间变化监听器接口
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context));
}
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
mDate = Calendar.getInstance(); // 获取当前时间
mInitialising = true; // 标记初始化状态
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; // 判断当前时间是否为AM
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();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); // 获取AM/PM选择器
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); // 设置AM/PM选择器最小值
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); // 设置AM/PM选择器最大值
mAmPmSpinner.setDisplayedValues(stringsForAmPm); // 设置AM/PM的显示值
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); // 设置AM/PM变化监听器
// 初始化时更新控件状态
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView); // 设置是否为24小时制
// 设置当前时间
setCurrentDate(date);
setEnabled(isEnabled()); // 设置是否启用
mInitialising = false; // 标记初始化完成
}
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
return;
}
super.setEnabled(enabled);
mDateSpinner.setEnabled(enabled); // 设置日期选择器启用状态
mMinuteSpinner.setEnabled(enabled); // 设置分钟选择器启用状态
mHourSpinner.setEnabled(enabled); // 设置小时选择器启用状态
mAmPmSpinner.setEnabled(enabled); // 设置AM/PM选择器启用状态
mIsEnabled = enabled; // 更新启用状态
}
@Override
public boolean isEnabled() {
return mIsEnabled; // 返回当前是否启用
}
// 获取当前时间的毫秒值
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
// 设置当前时间(以毫秒为单位)
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)); // 更新日期时间
}
// 设置当前时间(年、月、日、时、分)
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year); // 设置当前年份
setCurrentMonth(month); // 设置当前月份
setCurrentDay(dayOfMonth); // 设置当前日期
setCurrentHour(hourOfDay); // 设置当前小时
setCurrentMinute(minute); // 设置当前分钟
}
// 获取当前年份
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
// 设置当前年份
public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) {
return; // 如果年份没变化,则不更新
}
mDate.set(Calendar.YEAR, year); // 更新年份
updateDateControl(); // 更新日期显示
onDateTimeChanged(); // 触发日期时间变化事件
}
// 获取当前月份0-11
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
// 设置当前月份0-11
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) {
return; // 如果月份没变化,则不更新
}
mDate.set(Calendar.MONTH, month); // 更新月份
updateDateControl(); // 更新日期显示
onDateTimeChanged(); // 触发日期时间变化事件
}
// 获取当前日期
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
// 设置当前日期
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return; // 如果日期没变化,则不更新
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); // 更新日期
updateDateControl(); // 更新日期显示
onDateTimeChanged(); // 触发日期时间变化事件
}
// 获取当前小时24小时制
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
private int getCurrentHour() {
if (mIs24HourView){
return getCurrentHourOfDay(); // 如果是24小时制直接返回当前小时
} else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY; // 如果是PM减去12小时
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour; // 如果是0点设置为12
}
}
}
// 设置当前小时24小时制
public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return; // 如果小时没变化,则不更新
}
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); // 设置小时
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false; // 设置为PM
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY; // 减去12小时
}
} else {
mIsAm = true; // 设置为AM
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY; // 如果是0点设置为12
}
}
updateAmPmControl(); // 更新AM/PM控件
}
mHourSpinner.setValue(hourOfDay); // 更新小时选择器的值
onDateTimeChanged(); // 触发日期时间变化事件
}
// 获取当前分钟
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
// 设置当前分钟
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) {
return; // 如果分钟没变化,则不更新
}
mMinuteSpinner.setValue(minute); // 更新分钟选择器的值
mDate.set(Calendar.MINUTE, minute); // 设置分钟
onDateTimeChanged(); // 触发日期时间变化事件
}
// 返回是否为24小时制
public boolean is24HourView () {
return mIs24HourView;
}
// 设置是否为24小时制
public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) {
return; // 如果模式没变化,则不更新
}
mIs24HourView = is24HourView; // 设置为24小时制
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); // 隐藏AM/PM控件
int hour = getCurrentHourOfDay();
updateHourControl(); // 更新小时控件
setCurrentHour(hour); // 设置小时
updateAmPmControl(); // 更新AM/PM控件
}
// 更新日期控件的显示值
private void updateDateControl() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
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); // 格式化日期为“MM.dd 星期”
}
mDateSpinner.setDisplayedValues(mDateDisplayValues); // 设置日期选择器的显示值
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); // 设置初始值为当前日期
mDateSpinner.invalidate(); // 刷新日期选择器
}
// 更新AM/PM控件的显示
private void updateAmPmControl() {
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE); // 隐藏AM/PM控件
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index); // 设置AM/PM选择器的值
mAmPmSpinner.setVisibility(View.VISIBLE); // 显示AM/PM控件
}
}
// 更新小时控件的显示
private void updateHourControl() {
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
// 设置日期时间变化的回调接口
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
// 触发日期时间变化事件
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}

@ -0,0 +1,386 @@
/*
* 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.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名称
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账户名称偏好设置
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 上次同步时间的偏好设置
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色偏好设置
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账户的键
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账户类型过滤器键
private PreferenceCategory mAccountCategory; // 账户类别设置
private GTaskReceiver mReceiver; // 广播接收器
private Account[] mOriAccounts; // 原始账户列表
private boolean mHasAddedAccount; // 是否添加了账户标志
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
/* 使用应用图标作为导航 */
getActionBar().setDisplayHomeAsUpEnabled(true); // 显示向上按钮,允许导航回到上级界面
addPreferencesFromResource(R.xml.preferences); // 加载偏好设置资源文件
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); // 获取账户设置类别
mReceiver = new GTaskReceiver(); // 创建广播接收器
IntentFilter filter = new IntentFilter(); // 创建意图过滤器
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); // 监听同步服务广播
registerReceiver(mReceiver, filter); // 注册广播接收器
mOriAccounts = null; // 初始化原始账户列表
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); // 加载自定义头部视图
getListView().addHeaderView(header, null, true); // 将头部视图添加到列表视图中
}
@Override
protected void onResume() {
super.onResume();
// 如果用户添加了新的账户,需要自动设置同步账户
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts(); // 获取Google账户列表
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name); // 设置同步账户为新添加的账户
break;
}
}
}
}
refreshUI(); // 刷新UI界面
}
@Override
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver); // 注销广播接收器
}
super.onDestroy();
}
private void loadAccountPreference() {
mAccountCategory.removeAll(); // 清除当前账户类别下的所有偏好设置项
Preference accountPref = new Preference(this); // 创建账户偏好设置项
final String defaultAccount = getSyncAccountName(this); // 获取当前同步的账户名称
accountPref.setTitle(getString(R.string.preferences_account_title)); // 设置标题
accountPref.setSummary(getString(R.string.preferences_account_summary)); // 设置摘要
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) { // 如果没有正在同步
if (TextUtils.isEmpty(defaultAccount)) {
// 第一次设置账户
showSelectAccountAlertDialog(); // 显示选择账户对话框
} else {
// 如果已经设置了账户,需要提示用户风险
showChangeAccountConfirmAlertDialog(); // 显示更换账户确认对话框
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) // 显示不能更改账户的提示
.show();
}
return true;
}
});
mAccountCategory.addPreference(accountPref); // 添加账户偏好设置项到类别中
}
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button); // 获取同步按钮
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); // 获取同步状态文本视图
// 设置按钮状态
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel)); // 设置为取消同步按钮
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this); // 取消同步操作
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately)); // 设置为立即同步按钮
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this); // 启动同步操作
}
});
}
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); // 如果没有账户,禁用同步按钮
// 设置最后同步时间
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString()); // 显示当前同步进度
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this); // 获取最后同步时间
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime))); // 显示最后同步时间
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE); // 如果没有同步记录,隐藏显示
}
}
}
private void refreshUI() {
loadAccountPreference(); // 加载账户偏好设置
loadSyncButton(); // 加载同步按钮和状态
}
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); // 创建对话框构建器
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); // 加载对话框标题布局
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); // 获取标题文本
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); // 设置标题文本
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); // 获取副标题文本
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); // 设置副标题文本
dialogBuilder.setCustomTitle(titleView); // 设置自定义标题
dialogBuilder.setPositiveButton(null, null); // 设置空的正面按钮
Account[] accounts = getGoogleAccounts(); // 获取Google账户列表
String defAccount = getSyncAccountName(this); // 获取当前同步的账户名称
mOriAccounts = accounts; // 保存原始账户列表
mHasAddedAccount = false; // 标记未添加账户
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length]; // 创建账户列表项
final CharSequence[] itemMapping = items;
int checkedItem = -1; // 默认没有选中项
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index; // 如果账户与默认账户匹配,设置为选中
}
items[index++] = account.name; // 填充账户列表项
}
dialogBuilder.setSingleChoiceItems(items, checkedItem, // 设置单选列表
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString()); // 设置选中的账户为同步账户
dialog.dismiss(); // 关闭对话框
refreshUI(); // 刷新UI
}
});
}
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); // 加载添加账户视图
dialogBuilder.setView(addAccountView); // 设置添加账户视图
final AlertDialog dialog = dialogBuilder.show(); // 显示对话框
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true; // 标记账户已添加
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); // 启动添加账户设置页面
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1); // 启动设置页面
dialog.dismiss(); // 关闭对话框
}
});
}
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); // 创建对话框构建器
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); // 加载对话框标题布局
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); // 获取标题文本
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this))); // 设置标题为当前同步账户
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); // 获取副标题文本
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); // 设置副标题为警告信息
dialogBuilder.setCustomTitle(titleView); // 设置自定义标题
CharSequence[] menuItemArray = new CharSequence[] { // 设置菜单项
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog(); // 显示选择账户对话框
} else if (which == 1) {
removeSyncAccount(); // 移除同步账户
refreshUI(); // 刷新UI
}
}
});
dialogBuilder.show(); // 显示对话框
}
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this); // 获取账户管理器
return accountManager.getAccountsByType("com.google"); // 获取Google账户
}
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) { // 如果当前同步账户与新账户不同
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); // 获取共享偏好设置
SharedPreferences.Editor editor = settings.edit(); // 获取编辑器
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); // 保存新账户名称
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); // 清空账户名称
}
editor.commit(); // 提交更改
// 清除最后同步时间
setLastSyncTime(this, 0);
// 清除本地与gtask相关的信息
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues(); // 创建内容值
values.put(NoteColumns.GTASK_ID, ""); // 清空任务ID
values.put(NoteColumns.SYNC_ID, 0); // 清空同步ID
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); // 更新数据库
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account), // 显示设置账户成功的提示
Toast.LENGTH_SHORT).show();
}
}
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); // 获取共享偏好设置
SharedPreferences.Editor editor = settings.edit(); // 获取编辑器
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); // 移除同步账户名称
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME); // 移除最后同步时间
}
editor.commit(); // 提交更改
// 清除本地与gtask相关的信息
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues(); // 创建内容值
values.put(NoteColumns.GTASK_ID, ""); // 清空任务ID
values.put(NoteColumns.SYNC_ID, 0); // 清空同步ID
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); // 更新数据库
}
}).start();
}
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE); // 获取共享偏好设置
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); // 返回同步账户名称
}
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE); // 获取共享偏好设置
SharedPreferences.Editor editor = settings.edit(); // 获取编辑器
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); // 设置最后同步时间
editor.commit(); // 提交更改
}
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE); // 获取共享偏好设置
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); // 返回最后同步时间
}
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI(); // 刷新UI
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); // 获取同步状态视图
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); // 显示同步进度
}
}
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, NotesListActivity.class); // 创建返回到笔记列表的意图
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置清除栈标志
startActivity(intent); // 启动活动
return true;
default:
return false;
}
}
}

@ -0,0 +1,229 @@
/*
* 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;
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText"; // 定义日志标签
private int mIndex; // 当前编辑框的索引
private int mSelectionStartBeforeDelete; // 删除前的选择开始位置
// 定义常见的URL schema
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
// 存储URL schema对应的操作资源ID
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); // 电话链接
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); // 网页链接
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); // 邮件链接
}
/**
* {@link NoteEditActivity}
*/
public interface OnTextViewChangeListener {
/**
* {@link KeyEvent#KEYCODE_DEL}
*/
void onEditTextDelete(int index, String text);
/**
* {@link KeyEvent#KEYCODE_ENTER}
*/
void onEditTextEnter(int index, String text);
/**
*
*/
void onTextChange(int index, boolean hasText);
}
private OnTextViewChangeListener mOnTextViewChangeListener; // 文本变化监听器
// 构造函数
public NoteEditText(Context context) {
super(context, null);
mIndex = 0; // 默认索引为0
}
// 设置索引
public void setIndex(int index) {
mIndex = index;
}
// 设置文本变化监听器
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
// 处理触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取点击的坐标
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y); // 获取触摸点所在的行
int off = layout.getOffsetForHorizontal(line, x); // 获取触摸点在该行的偏移位置
Selection.setSelection(getText(), off); // 设置光标的位置
break;
}
return super.onTouchEvent(event); // 继续处理其他触摸事件
}
// 处理按键按下事件
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
return false; // 如果监听器已设置且按下回车键返回false
}
break;
case KeyEvent.KEYCODE_DEL:
mSelectionStartBeforeDelete = getSelectionStart(); // 记录删除前的光标位置
break;
default:
break;
}
return super.onKeyDown(keyCode, event); // 继续处理其他按键按下事件
}
// 处理按键抬起事件
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
// 如果设置了文本变化监听器且删除键按下时光标位置为0且不是第一个编辑框调用删除方法
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); // 继续处理其他按键抬起事件
}
// 当焦点变化时调用
@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); // 调用父类方法
}
// 创建上下文菜单
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) { // 如果文本是Spanned类型包含格式化信息
int selStart = getSelectionStart(); // 获取当前选中的起始位置
int selEnd = getSelectionEnd(); // 获取当前选中的结束位置
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); // 获取选中的URLSpan
if (urls.length == 1) { // 如果选中的只有一个URLSpan
int defaultResId = 0; // 默认资源ID为0
// 根据URL的schema选择相应的菜单项
for (String schema : sSchemaActionResMap.keySet()) {
if (urls[0].getURL().indexOf(schema) >= 0) {
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
defaultResId = R.string.note_link_other; // 如果没有匹配到schema使用默认资源
}
// 添加菜单项,并设置点击事件
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// 打开链接对应的Intent
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu); // 调用父类方法,继续创建菜单项
}
}
Loading…
Cancel
Save