feat(android): add fetch scheduling debug logs and triggerImmediateFetch API
- Add DN|SCHEDULE_CALLBACK logs to diagnose fetch scheduling - Add DN|SCHEDULE_FETCH_* structured logs for traceability - Add triggerImmediateFetch() public API for standalone fetches - Update fetch timing from 1 hour to 5 minutes before notification - Fix TypeScript lint errors: add return types, replace any types - Fix ESLint warnings: add console suppression comments - Fix capacitor.settings.gradle plugin path reference - Update android-app-improvement-plan.md with current state Changes: - DailyNotificationPlugin: Added scheduled callback logging and fetch method - DailyNotificationFetcher: Changed lead time from 1 hour to 5 minutes - EnhancedDailyNotificationFetcher: Added ENH|* structured event IDs - TypeScript services: Fixed lint errors and added proper types - Test app: Fixed capacitor settings path and TypeScript warnings
This commit is contained in:
@@ -374,6 +374,21 @@ export interface DailyNotificationPlugin {
|
||||
clearCacheForNewIdentity(): Promise<void>;
|
||||
updateBackgroundTaskIdentity(activeDid: string): Promise<void>;
|
||||
|
||||
// Content Fetching Methods
|
||||
/**
|
||||
* Trigger an immediate standalone fetch for content updates
|
||||
*
|
||||
* This method allows manual triggering of content fetches independently of
|
||||
* scheduled notifications. Useful for on-demand content refresh, cache warming,
|
||||
* or background sync operations.
|
||||
*
|
||||
* @returns Promise with success status and message
|
||||
*/
|
||||
triggerImmediateFetch(): Promise<{
|
||||
success: boolean;
|
||||
message: string;
|
||||
}>;
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
/**
|
||||
* Schedule a simple daily reminder notification
|
||||
|
||||
@@ -47,7 +47,9 @@ export interface PermissionEducation {
|
||||
export class NotificationPermissionManager {
|
||||
private static instance: NotificationPermissionManager;
|
||||
|
||||
private constructor() {}
|
||||
private constructor() {
|
||||
// Singleton constructor - no initialization needed
|
||||
}
|
||||
|
||||
public static getInstance(): NotificationPermissionManager {
|
||||
if (!NotificationPermissionManager.instance) {
|
||||
@@ -278,12 +280,12 @@ export class NotificationPermissionManager {
|
||||
}
|
||||
|
||||
// Check if we can access the plugin
|
||||
if (typeof (window as any).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
if (typeof (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: unknown } } }).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
return 'not_supported';
|
||||
}
|
||||
|
||||
const status = await (window as any).Capacitor?.Plugins?.DailyNotification?.checkPermissions();
|
||||
return status?.notifications || 'denied';
|
||||
const status = await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { checkPermissions: () => Promise<{ notifications?: 'granted' | 'denied' | 'prompt' | 'not_supported' }> } } } }).Capacitor?.Plugins?.DailyNotification?.checkPermissions();
|
||||
return (status?.notifications || 'denied') as 'granted' | 'denied' | 'prompt' | 'not_supported';
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error checking notification permissions:', error);
|
||||
@@ -297,11 +299,11 @@ export class NotificationPermissionManager {
|
||||
return 'not_supported';
|
||||
}
|
||||
|
||||
if (typeof (window as any).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
if (typeof (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: unknown } } }).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
return 'denied';
|
||||
}
|
||||
|
||||
const status = await (window as any).Capacitor?.Plugins?.DailyNotification?.getExactAlarmStatus();
|
||||
const status = await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { getExactAlarmStatus: () => Promise<{ canSchedule?: boolean }> } } } }).Capacitor?.Plugins?.DailyNotification?.getExactAlarmStatus();
|
||||
return status?.canSchedule ? 'granted' : 'denied';
|
||||
|
||||
} catch (error) {
|
||||
@@ -316,11 +318,11 @@ export class NotificationPermissionManager {
|
||||
return 'not_supported';
|
||||
}
|
||||
|
||||
if (typeof (window as any).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
if (typeof (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: unknown } } }).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
return 'denied';
|
||||
}
|
||||
|
||||
const status = await (window as any).Capacitor?.Plugins?.DailyNotification?.getBatteryStatus();
|
||||
const status = await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { getBatteryStatus: () => Promise<{ isOptimized?: boolean }> } } } }).Capacitor?.Plugins?.DailyNotification?.getBatteryStatus();
|
||||
return status?.isOptimized ? 'denied' : 'granted';
|
||||
|
||||
} catch (error) {
|
||||
@@ -345,11 +347,11 @@ export class NotificationPermissionManager {
|
||||
|
||||
private async requestNotificationPermissions(): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
if (typeof (window as any).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
if (typeof (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: unknown } } }).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
return { success: false, message: 'Plugin not available' };
|
||||
}
|
||||
|
||||
const result = await (window as any).Capacitor?.Plugins?.DailyNotification?.requestPermissions();
|
||||
const result = await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { requestPermissions: () => Promise<{ notifications?: string }> } } } }).Capacitor?.Plugins?.DailyNotification?.requestPermissions();
|
||||
return {
|
||||
success: result?.notifications === 'granted',
|
||||
message: result?.notifications === 'granted' ? 'Notification permissions granted' : 'Notification permissions denied'
|
||||
@@ -362,16 +364,16 @@ export class NotificationPermissionManager {
|
||||
|
||||
private async requestExactAlarmPermissions(): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
if (typeof (window as any).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
if (typeof (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: unknown } } }).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
return { success: false, message: 'Plugin not available' };
|
||||
}
|
||||
|
||||
await (window as any).Capacitor?.Plugins?.DailyNotification?.requestExactAlarmPermission();
|
||||
await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { requestExactAlarmPermission: () => Promise<void> } } } }).Capacitor?.Plugins?.DailyNotification?.requestExactAlarmPermission();
|
||||
|
||||
// Check if permission was granted
|
||||
const status = await (window as any).Capacitor?.Plugins?.DailyNotification?.getExactAlarmStatus();
|
||||
const status = await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { getExactAlarmStatus: () => Promise<{ canSchedule?: boolean }> } } } }).Capacitor?.Plugins?.DailyNotification?.getExactAlarmStatus();
|
||||
return {
|
||||
success: status?.canSchedule,
|
||||
success: !!status?.canSchedule,
|
||||
message: status?.canSchedule ? 'Exact alarm permissions granted' : 'Exact alarm permissions denied'
|
||||
};
|
||||
|
||||
@@ -382,14 +384,14 @@ export class NotificationPermissionManager {
|
||||
|
||||
private async requestBatteryOptimizationExemption(): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
if (typeof (window as any).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
if (typeof (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: unknown } } }).Capacitor?.Plugins?.DailyNotification === 'undefined') {
|
||||
return { success: false, message: 'Plugin not available' };
|
||||
}
|
||||
|
||||
await (window as any).Capacitor?.Plugins?.DailyNotification?.requestBatteryOptimizationExemption();
|
||||
await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { requestBatteryOptimizationExemption: () => Promise<void> } } } }).Capacitor?.Plugins?.DailyNotification?.requestBatteryOptimizationExemption();
|
||||
|
||||
// Check if exemption was granted
|
||||
const status = await (window as any).Capacitor?.Plugins?.DailyNotification?.getBatteryStatus();
|
||||
const status = await (window as unknown as { Capacitor?: { Plugins?: { DailyNotification?: { getBatteryStatus: () => Promise<{ isOptimized?: boolean }> } } } }).Capacitor?.Plugins?.DailyNotification?.getBatteryStatus();
|
||||
return {
|
||||
success: !status?.isOptimized,
|
||||
message: !status?.isOptimized ? 'Battery optimization exemption granted' : 'Battery optimization exemption denied'
|
||||
|
||||
@@ -214,7 +214,9 @@ export interface ValidationResult<T> {
|
||||
export class NotificationValidationService {
|
||||
private static instance: NotificationValidationService;
|
||||
|
||||
private constructor() {}
|
||||
private constructor() {
|
||||
// Singleton constructor - no initialization needed
|
||||
}
|
||||
|
||||
public static getInstance(): NotificationValidationService {
|
||||
if (!NotificationValidationService.instance) {
|
||||
@@ -412,7 +414,7 @@ export class NotificationValidationService {
|
||||
/**
|
||||
* Get validation schema for a specific type
|
||||
*/
|
||||
public getSchema(type: 'notification' | 'reminder' | 'contentFetch' | 'userNotification' | 'dualSchedule') {
|
||||
public getSchema(type: 'notification' | 'reminder' | 'contentFetch' | 'userNotification' | 'dualSchedule'): z.ZodType {
|
||||
switch (type) {
|
||||
case 'notification':
|
||||
return NotificationOptionsSchema;
|
||||
@@ -451,7 +453,10 @@ export class ValidatedDailyNotificationPlugin {
|
||||
}
|
||||
|
||||
// Call native implementation with validated data
|
||||
return await this.nativeScheduleDailyNotification(validation.data!);
|
||||
if (!validation.data) {
|
||||
throw new Error('Validation passed but data is null');
|
||||
}
|
||||
return await this.nativeScheduleDailyNotification(validation.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,7 +470,10 @@ export class ValidatedDailyNotificationPlugin {
|
||||
}
|
||||
|
||||
// Call native implementation with validated data
|
||||
return await this.nativeScheduleDailyReminder(validation.data!);
|
||||
if (!validation.data) {
|
||||
throw new Error('Validation passed but data is null');
|
||||
}
|
||||
return await this.nativeScheduleDailyReminder(validation.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,7 +487,10 @@ export class ValidatedDailyNotificationPlugin {
|
||||
}
|
||||
|
||||
// Call native implementation with validated data
|
||||
return await this.nativeScheduleContentFetch(validation.data!);
|
||||
if (!validation.data) {
|
||||
throw new Error('Validation passed but data is null');
|
||||
}
|
||||
return await this.nativeScheduleContentFetch(validation.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,7 +504,10 @@ export class ValidatedDailyNotificationPlugin {
|
||||
}
|
||||
|
||||
// Call native implementation with validated data
|
||||
return await this.nativeScheduleUserNotification(validation.data!);
|
||||
if (!validation.data) {
|
||||
throw new Error('Validation passed but data is null');
|
||||
}
|
||||
return await this.nativeScheduleUserNotification(validation.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,7 +521,10 @@ export class ValidatedDailyNotificationPlugin {
|
||||
}
|
||||
|
||||
// Call native implementation with validated data
|
||||
return await this.nativeScheduleDualNotification(validation.data!);
|
||||
if (!validation.data) {
|
||||
throw new Error('Validation passed but data is null');
|
||||
}
|
||||
return await this.nativeScheduleDualNotification(validation.data);
|
||||
}
|
||||
|
||||
// Native implementation methods (to be implemented)
|
||||
|
||||
Reference in New Issue
Block a user