家课表App提醒通知功能技术解析
继续聊聊家课表App,这次是提醒通知功能。想体验完整功能,可以去鸿蒙应用市场搜索"家课表"下载。
写在前面
上一篇我们聊了家课表App的作业管理功能,今天聊聊提醒通知——怎么让用户不忘记重要的事情。
提醒通知是教育类App的重要功能。Web端用浏览器通知API,鸿蒙端用系统通知和提醒服务,功能更强大、更可靠。而且鸿蒙端支持后台提醒,即使App没打开也能收到提醒。
今天这篇,我会从系统通知、定时提醒、日历集成这几个方面,聊聊家课表App的提醒通知功能。
1. 系统通知:即时提醒
ArkTS系统通知:
import { notificationManager } from '@kit.NotificationManagementKit';
class NotificationService {
// 发送通知
static async sendNotification(title: string, content: string, id: number = 1001) {
const notificationRequest: notificationManager.NotificationRequest = {
id: id,
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: title,
text: content
}
}
};
try {
await notificationManager.publish(notificationRequest);
} catch (err) {
console.error('发送通知失败:', err);
}
}
// 取消通知
static async cancelNotification(id: number) {
try {
await notificationManager.cancel(id);
} catch (err) {
console.error('取消通知失败:', err);
}
}
// 取消所有通知
static async cancelAll() {
try {
await notificationManager.cancelAll();
} catch (err) {
console.error('取消所有通知失败:', err);
}
}
}
2. 定时提醒:后台提醒
ArkTS定时提醒:
import { reminderAgentManager } from '@kit.BackgroundTasksKit';
class ReminderService {
// 创建一次性提醒
static async createOnceReminder(
title: string,
content: string,
triggerTime: number,
notificationId: number
) {
const date = new Date(triggerTime);
const reminderInfo: reminderAgentManager.ReminderRequestCalendar = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_CALENDAR,
dateTime: {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
hour: date.getHours(),
minute: date.getMinutes()
},
wantAgent: {
wants: [
{
bundleName: 'com.example.jiakbiao',
abilityName: 'EntryAbility'
}
],
actionType: 0
},
title: title,
content: content,
notificationId: notificationId
};
try {
await reminderAgentManager.publishReminder(reminderInfo);
} catch (err) {
console.error('创建提醒失败:', err);
}
}
// 创建每日提醒
static async createDailyReminder(
title: string,
content: string,
hour: number,
minute: number,
notificationId: number
) {
const reminderInfo: reminderAgentManager.ReminderRequestAlarm = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_ALARM,
hour: hour,
minute: minute,
daysOfWeek: [1, 2, 3, 4, 5], // 周一到周五
wantAgent: {
wants: [
{
bundleName: 'com.example.jiakbiao',
abilityName: 'EntryAbility'
}
],
actionType: 0
},
title: title,
content: content,
notificationId: notificationId
};
try {
await reminderAgentManager.publishReminder(reminderInfo);
} catch (err) {
console.error('创建提醒失败:', err);
}
}
// 取消提醒
static async cancelReminder(notificationId: number) {
try {
await reminderAgentManager.cancelReminder(notificationId);
} catch (err) {
console.error('取消提醒失败:', err);
}
}
}
3. 提醒管理:统一管理
ArkTS提醒管理:
interface Reminder {
id: string;
title: string;
content: string;
type: 'once' | 'daily' | 'weekly';
triggerTime?: number;
hour?: number;
minute?: number;
daysOfWeek?: number[];
notificationId: number;
enabled: boolean;
}
@Component
struct ReminderManager {
@State reminders: Reminder[] = [];
@State showCreateDialog: boolean = false;
private rdbStore: relationalStore.RdbStore | null = null;
async aboutToAppear() {
await this.initDatabase();
await this.loadReminders();
}
async initDatabase() {
const config: relationalStore.StoreConfig = {
name: 'reminders.db',
securityLevel: relationalStore.SecurityLevel.S1
};
this.rdbStore = await relationalStore.getRdbStore(getContext(), config);
await this.rdbStore.executeSql(`
CREATE TABLE IF NOT EXISTS reminders (
id TEXT PRIMARY KEY,
title TEXT,
content TEXT,
type TEXT,
trigger_time INTEGER,
hour INTEGER,
minute INTEGER,
days_of_week TEXT,
notification_id INTEGER,
enabled INTEGER DEFAULT 1
)
`);
}
async loadReminders() {
if (!this.rdbStore) return;
const resultSet = await this.rdbStore.querySql(
'SELECT * FROM reminders ORDER BY trigger_time ASC'
);
const list: Reminder[] = [];
while (resultSet.goToNextRow()) {
list.push({
id: resultSet.getString(resultSet.getColumnIndex('id')),
title: resultSet.getString(resultSet.getColumnIndex('title')),
content: resultSet.getString(resultSet.getColumnIndex('content')),
type: resultSet.getString(resultSet.getColumnIndex('type')) as any,
triggerTime: resultSet.getLong(resultSet.getColumnIndex('trigger_time')),
hour: resultSet.getLong(resultSet.getColumnIndex('hour')),
minute: resultSet.getLong(resultSet.getColumnIndex('minute')),
daysOfWeek: JSON.parse(resultSet.getString(resultSet.getColumnIndex('days_of_week')) || '[]'),
notificationId: resultSet.getLong(resultSet.getColumnIndex('notification_id')),
enabled: resultSet.getLong(resultSet.getColumnIndex('enabled')) === 1
});
}
resultSet.close();
this.reminders = list;
}
async toggleReminder(reminder: Reminder) {
const newEnabled = !reminder.enabled;
if (newEnabled) {
await this.enableReminder(reminder);
} else {
await this.disableReminder(reminder);
}
await this.loadReminders();
}
async enableReminder(reminder: Reminder) {
if (reminder.type === 'once' && reminder.triggerTime) {
await ReminderService.createOnceReminder(
reminder.title,
reminder.content,
reminder.triggerTime,
reminder.notificationId
);
} else if (reminder.type === 'daily' && reminder.hour !== undefined && reminder.minute !== undefined) {
await ReminderService.createDailyReminder(
reminder.title,
reminder.content,
reminder.hour,
reminder.minute,
reminder.notificationId
);
}
// 更新数据库
if (this.rdbStore) {
await this.rdbStore.executeSql(
'UPDATE reminders SET enabled = 1 WHERE id = ?',
[reminder.id]
);
}
}
async disableReminder(reminder: Reminder) {
await ReminderService.cancelReminder(reminder.notificationId);
if (this.rdbStore) {
await this.rdbStore.executeSql(
'UPDATE reminders SET enabled = 0 WHERE id = ?',
[reminder.id]
);
}
}
async deleteReminder(reminder: Reminder) {
await ReminderService.cancelReminder(reminder.notificationId);
if (this.rdbStore) {
await this.rdbStore.executeSql('DELETE FROM reminders WHERE id = ?', [reminder.id]);
}
await this.loadReminders();
}
build() {
Column() {
Text('提醒管理')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
List({ space: 8 }) {
ForEach(this.reminders, (reminder: Reminder) => {
ListItem() {
this.ReminderCard(reminder)
}
})
}
.margin({ top: 16 })
.layoutWeight(1)
Button('添加提醒')
.width('90%')
.margin({ bottom: 20 })
.onClick(() => this.showCreateDialog = true)
}
}
@Builder
ReminderCard(reminder: Reminder) {
Row() {
Column() {
Text(reminder.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text(reminder.content)
.fontSize(14)
.fontColor('#666')
.margin({ top: 4 })
Text(this.getReminderTimeText(reminder))
.fontSize(12)
.fontColor('#999')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: reminder.enabled })
.onChange((isOn: boolean) => {
this.toggleReminder(reminder);
})
}
.padding(12)
.backgroundColor('#fff')
.borderRadius(8)
}
getReminderTimeText(reminder: Reminder): string {
if (reminder.type === 'once' && reminder.triggerTime) {
return new Date(reminder.triggerTime).toLocaleString();
}
if (reminder.type === 'daily' && reminder.hour !== undefined && reminder.minute !== undefined) {
const days = reminder.daysOfWeek?.map(d => ['日', '一', '二', '三', '四', '五', '六'][d]).join('、') || '';
return `每天 ${reminder.hour}:${reminder.minute.toString().padStart(2, '0')}`;
}
return '';
}
}
4. 通知权限:权限管理
ArkTS权限管理:
class PermissionManager {
// 检查通知权限
static async checkNotificationPermission(): Promise<boolean> {
try {
const result = await notificationManager.isNotificationEnabled();
return result;
} catch (err) {
console.error('检查通知权限失败:', err);
return false;
}
}
// 请求通知权限
static async requestNotificationPermission() {
try {
await notificationManager.requestEnableNotification();
} catch (err) {
console.error('请求通知权限失败:', err);
}
}
// 检查后台提醒权限
static async checkReminderPermission(): Promise<boolean> {
try {
const result = await reminderAgentManager.canRequest();
return result;
} catch (err) {
console.error('检查提醒权限失败:', err);
return false;
}
}
}
5. 通知设置:个性化配置
ArkTS通知设置:
@Component
struct NotificationSettings {
@State enableHomeworkReminder: boolean = true;
@State reminderTime: number = 18; // 18:00
@State enableSound: boolean = true;
@State enableVibration: boolean = true;
build() {
Column() {
Text('通知设置')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
// 作业提醒
Row() {
Column() {
Text('作业提醒')
.fontSize(16)
Text('在截止日期前提醒完成作业')
.fontSize(14)
.fontColor('#666')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.enableHomeworkReminder })
.onChange((isOn: boolean) => {
this.enableHomeworkReminder = isOn;
})
}
.padding(16)
.backgroundColor('#fff')
.borderRadius(12)
.margin({ top: 16 })
// 提醒时间
if (this.enableHomeworkReminder) {
Row() {
Text('提醒时间')
.fontSize(14)
Text(`${this.reminderTime}:00`)
.fontSize(14)
.margin({ left: 'auto' })
}
.padding(16)
.backgroundColor('#fff')
.borderRadius(12)
.margin({ top: 8 })
}
// 声音设置
Row() {
Text('提醒声音')
.fontSize(14)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.enableSound })
.onChange((isOn: boolean) => {
this.enableSound = isOn;
})
}
.padding(16)
.backgroundColor('#fff')
.borderRadius(12)
.margin({ top: 8 })
// 震动设置
Row() {
Text('震动提醒')
.fontSize(14)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.enableVibration })
.onChange((isOn: boolean) => {
this.enableVibration = isOn;
})
}
.padding(16)
.backgroundColor('#fff')
.borderRadius(12)
.margin({ top: 8 })
}
}
}
总结
家课表App的提醒通知功能,从系统通知、定时提醒、提醒管理到权限管理和通知设置,每一部分都有它的技术要点。鸿蒙端的通知API和提醒服务,提供了强大的后台提醒能力。
如果你做教育或生活管理类App,这些通知技术都很实用。系统通知、定时提醒、权限管理,这些都是通用的技术。
家课表App就聊到这里。下一篇文章,我们聊聊绘本架App的阅读计时功能。