Browse Source

fix(test-app): implement Schedule Notification button functionality

- Replace empty TODO with actual plugin integration
- Add dynamic import of DailyNotification plugin
- Implement proper error handling with try/catch
- Add user feedback via alert dialogs
- Add comprehensive logging for debugging
- Fix TypeScript priority type with 'high' as const
- Successfully schedules notifications with AlarmManager
- Verified alarm appears in dumpsys alarm output

The Schedule Notification button now actually calls the native
plugin and schedules notifications instead of being a placeholder.
master
Matthew Raymer 2 days ago
parent
commit
4a8573ec87
  1. 36
      test-apps/daily-notification-test/src/views/ScheduleView.vue
  2. 644
      www/index.html

36
test-apps/daily-notification-test/src/views/ScheduleView.vue

@ -52,7 +52,41 @@ class ScheduleView extends Vue {
async scheduleNotification() { async scheduleNotification() {
this.isScheduling = true this.isScheduling = true
try { try {
// TODO: call plugin console.log('🔄 Starting notification scheduling...')
// Import and use the real plugin
const { DailyNotification } = await import('@timesafari/daily-notification-plugin')
const plugin = DailyNotification
console.log('✅ Plugin loaded:', plugin)
const options = {
time: this.scheduleTime,
title: this.notificationTitle,
body: this.notificationMessage,
sound: true,
priority: 'high' as const
}
console.log('📅 Scheduling notification with options:', options)
await plugin.scheduleDailyNotification(options)
console.log('✅ Notification scheduled successfully!')
// Show success feedback to user
alert('Notification scheduled successfully!')
} catch (error) {
console.error('❌ Failed to schedule notification:', error)
console.error('❌ Error details:', {
name: error.name,
message: error.message,
stack: error.stack
})
// Show error feedback to user
alert(`Failed to schedule notification: ${error.message}`)
} finally { } finally {
this.isScheduling = false this.isScheduling = false
} }

644
www/index.html

@ -17,149 +17,605 @@
color: white; color: white;
} }
.container { .container {
max-width: 600px; max-width: 800px;
margin: 0 auto; margin: 0 auto;
text-align: center;
} }
h1 { h1 {
text-align: center;
margin-bottom: 30px; margin-bottom: 30px;
font-size: 2.5em; font-size: 2.5em;
} }
.section {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 20px;
margin: 20px 0;
}
.section h2 {
margin-top: 0;
color: #ffd700;
}
.button { .button {
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
border: 2px solid rgba(255, 255, 255, 0.3); border: 2px solid rgba(255, 255, 255, 0.3);
color: white; color: white;
padding: 15px 30px; padding: 12px 24px;
margin: 10px; margin: 8px;
border-radius: 25px; border-radius: 20px;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 14px;
transition: all 0.3s ease; transition: all 0.3s ease;
display: inline-block;
} }
.button:hover { .button:hover {
background: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px); transform: translateY(-2px);
} }
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.status { .status {
margin-top: 30px; margin-top: 15px;
padding: 20px; padding: 15px;
background: rgba(255, 255, 255, 0.1); background: rgba(0, 0, 0, 0.2);
border-radius: 10px; border-radius: 8px;
font-family: monospace; font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
max-height: 200px;
overflow-y: auto;
}
.success { color: #4CAF50; }
.error { color: #f44336; }
.warning { color: #ff9800; }
.info { color: #2196F3; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 15px 0;
}
.input-group {
margin: 10px 0;
}
.input-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.input-group input, .input-group select {
width: 100%;
padding: 8px;
border-radius: 5px;
border: 1px solid rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.1);
color: white;
}
.input-group input::placeholder {
color: rgba(255, 255, 255, 0.7);
} }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>🔔 DailyNotification Plugin Test</h1> <h1>🔔 DailyNotification Plugin Test</h1>
<p>Test the DailyNotification plugin functionality</p>
<button class="button" onclick="testPlugin()">Test Plugin</button> <!-- Plugin Status Section -->
<button class="button" onclick="configurePlugin()">Configure Plugin</button> <div class="section">
<button class="button" onclick="checkStatus()">Check Status</button> <h2>📊 Plugin Status</h2>
<div class="grid">
<div id="status" class="status"> <button class="button" onclick="checkPluginAvailability()">Check Availability</button>
Ready to test... <button class="button" onclick="getNotificationStatus()">Get Status</button>
<button class="button" onclick="checkPermissions()">Check Permissions</button>
<button class="button" onclick="getBatteryStatus()">Battery Status</button>
</div>
<div id="status" class="status">Ready to test...</div>
</div>
<!-- Permission Management Section -->
<div class="section">
<h2>🔐 Permission Management</h2>
<div class="grid">
<button class="button" onclick="requestPermissions()">Request Permissions</button>
<button class="button" onclick="requestExactAlarmPermission()">Request Exact Alarm</button>
<button class="button" onclick="openExactAlarmSettings()">Open Settings</button>
<button class="button" onclick="requestBatteryOptimizationExemption()">Battery Exemption</button>
</div>
</div>
<!-- Notification Scheduling Section -->
<div class="section">
<h2>⏰ Notification Scheduling</h2>
<div class="input-group">
<label for="notificationUrl">Content URL:</label>
<input type="text" id="notificationUrl" placeholder="https://api.example.com/daily-content" value="https://api.example.com/daily-content">
</div>
<div class="input-group">
<label for="notificationTime">Schedule Time:</label>
<input type="time" id="notificationTime" value="09:00">
</div>
<div class="input-group">
<label for="notificationTitle">Title:</label>
<input type="text" id="notificationTitle" placeholder="Daily Notification" value="Daily Notification">
</div>
<div class="input-group">
<label for="notificationBody">Body:</label>
<input type="text" id="notificationBody" placeholder="Your daily content is ready!" value="Your daily content is ready!">
</div>
<div class="grid">
<button class="button" onclick="scheduleNotification()">Schedule Notification</button>
<button class="button" onclick="cancelAllNotifications()">Cancel All</button>
<button class="button" onclick="getLastNotification()">Get Last</button>
</div>
</div>
<!-- Configuration Section -->
<div class="section">
<h2>⚙️ Plugin Configuration</h2>
<div class="input-group">
<label for="configUrl">Fetch URL:</label>
<input type="text" id="configUrl" placeholder="https://api.example.com/content" value="https://api.example.com/content">
</div>
<div class="input-group">
<label for="configTime">Schedule Time:</label>
<input type="time" id="configTime" value="09:00">
</div>
<div class="input-group">
<label for="configRetryCount">Retry Count:</label>
<input type="number" id="configRetryCount" value="3" min="0" max="10">
</div>
<div class="grid">
<button class="button" onclick="configurePlugin()">Configure Plugin</button>
<button class="button" onclick="updateSettings()">Update Settings</button>
</div>
</div>
<!-- Advanced Features Section -->
<div class="section">
<h2>🚀 Advanced Features</h2>
<div class="grid">
<button class="button" onclick="getExactAlarmStatus()">Exact Alarm Status</button>
<button class="button" onclick="getRebootRecoveryStatus()">Reboot Recovery</button>
<button class="button" onclick="getRollingWindowStats()">Rolling Window</button>
<button class="button" onclick="maintainRollingWindow()">Maintain Window</button>
<button class="button" onclick="getContentCache()">Content Cache</button>
<button class="button" onclick="clearContentCache()">Clear Cache</button>
<button class="button" onclick="getContentHistory()">Content History</button>
<button class="button" onclick="getDualScheduleStatus()">Dual Schedule</button>
</div>
</div> </div>
</div> </div>
<script> <script>
console.log('Script loading...'); console.log('🔔 DailyNotification Plugin Test Interface Loading...');
// Mock DailyNotification plugin for WebView testing // Global variables
if (!window.DailyNotification) { let plugin = null;
console.log('Creating mock DailyNotification plugin...'); let isPluginAvailable = false;
window.DailyNotification = {
configure: function(config) { // Initialize plugin on page load
console.log('Mock configure called with:', config); document.addEventListener('DOMContentLoaded', async function() {
console.log('📱 DOM loaded, initializing plugin...');
await initializePlugin();
});
// Initialize the real DailyNotification plugin
async function initializePlugin() {
try {
// Try to access the real plugin through Capacitor
if (window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.DailyNotification) {
plugin = window.Capacitor.Plugins.DailyNotification;
isPluginAvailable = true;
console.log('✅ Real DailyNotification plugin found!');
updateStatus('success', '✅ Real DailyNotification plugin loaded successfully!');
} else {
// Fallback to mock for development
console.log('⚠️ Real plugin not available, using mock for development');
plugin = createMockPlugin();
isPluginAvailable = false;
updateStatus('warning', '⚠️ Using mock plugin (real plugin not available)');
}
} catch (error) {
console.error('❌ Plugin initialization failed:', error);
updateStatus('error', `❌ Plugin initialization failed: ${error.message}`);
}
}
// Create mock plugin for development/testing
function createMockPlugin() {
return {
configure: async (options) => {
console.log('Mock configure called with:', options);
return Promise.resolve();
},
getNotificationStatus: async () => {
return Promise.resolve({
isEnabled: true,
isScheduled: true,
lastNotificationTime: Date.now() - 86400000,
nextNotificationTime: Date.now() + 3600000,
pending: 1,
settings: { url: 'https://api.example.com/content', time: '09:00' },
error: null
});
},
checkPermissions: async () => {
return Promise.resolve({
notifications: 'granted',
backgroundRefresh: 'granted',
alert: true,
badge: true,
sound: true
});
},
requestPermissions: async () => {
return Promise.resolve({
notifications: 'granted',
backgroundRefresh: 'granted',
alert: true,
badge: true,
sound: true
});
},
scheduleDailyNotification: async (options) => {
console.log('Mock scheduleDailyNotification called with:', options);
return Promise.resolve();
},
cancelAllNotifications: async () => {
console.log('Mock cancelAllNotifications called');
return Promise.resolve();
},
getLastNotification: async () => {
return Promise.resolve({
id: 'mock-123',
title: 'Mock Notification',
body: 'This is a mock notification',
timestamp: Date.now() - 3600000,
url: 'https://example.com'
});
},
getBatteryStatus: async () => {
return Promise.resolve({
level: 85,
isCharging: false,
powerState: 1,
isOptimizationExempt: false
});
},
getExactAlarmStatus: async () => {
return Promise.resolve({
supported: true,
enabled: true,
canSchedule: true,
fallbackWindow: '±15 minutes'
});
},
requestExactAlarmPermission: async () => {
console.log('Mock requestExactAlarmPermission called');
return Promise.resolve();
},
openExactAlarmSettings: async () => {
console.log('Mock openExactAlarmSettings called');
return Promise.resolve();
},
requestBatteryOptimizationExemption: async () => {
console.log('Mock requestBatteryOptimizationExemption called');
return Promise.resolve();
},
getRebootRecoveryStatus: async () => {
return Promise.resolve({
inProgress: false,
lastRecoveryTime: Date.now() - 86400000,
timeSinceLastRecovery: 86400000,
recoveryNeeded: false
});
},
getRollingWindowStats: async () => {
return Promise.resolve({
stats: 'Window: 7 days, Notifications: 5, Success rate: 100%',
maintenanceNeeded: false,
timeUntilNextMaintenance: 3600000
});
},
maintainRollingWindow: async () => {
console.log('Mock maintainRollingWindow called');
return Promise.resolve();
},
getContentCache: async () => {
return Promise.resolve({
'cache-key-1': { content: 'Mock cached content', timestamp: Date.now() },
'cache-key-2': { content: 'Another mock item', timestamp: Date.now() - 3600000 }
});
},
clearContentCache: async () => {
console.log('Mock clearContentCache called');
return Promise.resolve(); return Promise.resolve();
}, },
getStatus: function() { getContentHistory: async () => {
return Promise.resolve([
{ id: '1', timestamp: Date.now() - 86400000, success: true, content: 'Mock content 1' },
{ id: '2', timestamp: Date.now() - 172800000, success: true, content: 'Mock content 2' }
]);
},
getDualScheduleStatus: async () => {
return Promise.resolve({ return Promise.resolve({
configured: true, isActive: true,
platform: 'web-mock', contentSchedule: { nextRun: Date.now() + 3600000, isEnabled: true },
lastFetch: new Date().toISOString() userSchedule: { nextRun: Date.now() + 7200000, isEnabled: true },
lastContentFetch: Date.now() - 3600000,
lastUserNotification: Date.now() - 7200000
}); });
} }
}; };
} }
// Define functions immediately and attach to window // Utility function to update status display
function testPlugin() { function updateStatus(type, message) {
console.log('testPlugin called'); const statusEl = document.getElementById('status');
const status = document.getElementById('status'); statusEl.className = `status ${type}`;
status.innerHTML = 'Testing plugin...'; statusEl.textContent = message;
console.log(`[${type.toUpperCase()}] ${message}`);
}
// Plugin availability check
async function checkPluginAvailability() {
updateStatus('info', '🔍 Checking plugin availability...');
try { try {
if (!window.DailyNotification) { if (plugin) {
status.innerHTML = 'DailyNotification plugin not available'; updateStatus('success', `✅ Plugin available: ${isPluginAvailable ? 'Real plugin' : 'Mock plugin'}`);
return; } else {
updateStatus('error', '❌ Plugin not available');
} }
// Plugin is loaded and ready
status.innerHTML = 'Plugin is loaded and ready!';
} catch (error) { } catch (error) {
status.innerHTML = `Plugin test failed: ${error.message}`; updateStatus('error', `❌ Availability check failed: ${error.message}`);
} }
} }
function configurePlugin() { // Get notification status
console.log('configurePlugin called'); async function getNotificationStatus() {
const status = document.getElementById('status'); updateStatus('info', '📊 Getting notification status...');
status.innerHTML = 'Configuring plugin...'; try {
const status = await plugin.getNotificationStatus();
try { updateStatus('success', `📊 Status: ${JSON.stringify(status, null, 2)}`);
if (!window.DailyNotification) {
status.innerHTML = 'DailyNotification plugin not available';
return;
}
window.DailyNotification.configure({
fetchUrl: 'https://api.example.com/daily-content',
scheduleTime: '09:00',
enableNotifications: true
})
.then(() => {
status.innerHTML = 'Plugin configured successfully!';
})
.catch(error => {
status.innerHTML = `Configuration failed: ${error.message}`;
});
} catch (error) { } catch (error) {
status.innerHTML = `Configuration failed: ${error.message}`; updateStatus('error', `❌ Status check failed: ${error.message}`);
} }
} }
function checkStatus() { // Check permissions
console.log('checkStatus called'); async function checkPermissions() {
const status = document.getElementById('status'); updateStatus('info', '🔐 Checking permissions...');
status.innerHTML = 'Checking plugin status...'; try {
const permissions = await plugin.checkPermissions();
try { updateStatus('success', `🔐 Permissions: ${JSON.stringify(permissions, null, 2)}`);
if (!window.DailyNotification) {
status.innerHTML = 'DailyNotification plugin not available';
return;
}
window.DailyNotification.getStatus()
.then(result => {
status.innerHTML = `Plugin status: ${JSON.stringify(result, null, 2)}`;
})
.catch(error => {
status.innerHTML = `Status check failed: ${error.message}`;
});
} catch (error) { } catch (error) {
status.innerHTML = `Status check failed: ${error.message}`; updateStatus('error', `❌ Permission check failed: ${error.message}`);
} }
} }
// Attach to window object // Request permissions
window.testPlugin = testPlugin; async function requestPermissions() {
window.configurePlugin = configurePlugin; updateStatus('info', '🔐 Requesting permissions...');
window.checkStatus = checkStatus; try {
const result = await plugin.requestPermissions();
console.log('Functions attached to window:', { updateStatus('success', `🔐 Permission result: ${JSON.stringify(result, null, 2)}`);
testPlugin: typeof window.testPlugin, } catch (error) {
configurePlugin: typeof window.configurePlugin, updateStatus('error', `❌ Permission request failed: ${error.message}`);
checkStatus: typeof window.checkStatus }
}); }
// Get battery status
async function getBatteryStatus() {
updateStatus('info', '🔋 Getting battery status...');
try {
const battery = await plugin.getBatteryStatus();
updateStatus('success', `🔋 Battery: ${JSON.stringify(battery, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Battery check failed: ${error.message}`);
}
}
// Schedule notification
async function scheduleNotification() {
updateStatus('info', '⏰ Scheduling notification...');
try {
const options = {
url: document.getElementById('notificationUrl').value,
time: document.getElementById('notificationTime').value,
title: document.getElementById('notificationTitle').value,
body: document.getElementById('notificationBody').value,
sound: true,
priority: 'high'
};
await plugin.scheduleDailyNotification(options);
updateStatus('success', `⏰ Notification scheduled: ${JSON.stringify(options, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Scheduling failed: ${error.message}`);
}
}
// Cancel all notifications
async function cancelAllNotifications() {
updateStatus('info', '❌ Cancelling all notifications...');
try {
await plugin.cancelAllNotifications();
updateStatus('success', '❌ All notifications cancelled');
} catch (error) {
updateStatus('error', `❌ Cancel failed: ${error.message}`);
}
}
// Get last notification
async function getLastNotification() {
updateStatus('info', '📱 Getting last notification...');
try {
const notification = await plugin.getLastNotification();
updateStatus('success', `📱 Last notification: ${JSON.stringify(notification, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Get last notification failed: ${error.message}`);
}
}
// Configure plugin
async function configurePlugin() {
updateStatus('info', '⚙️ Configuring plugin...');
try {
const config = {
fetchUrl: document.getElementById('configUrl').value,
scheduleTime: document.getElementById('configTime').value,
retryCount: parseInt(document.getElementById('configRetryCount').value),
enableNotifications: true,
offlineFallback: true
};
await plugin.configure(config);
updateStatus('success', `⚙️ Plugin configured: ${JSON.stringify(config, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Configuration failed: ${error.message}`);
}
}
// Update settings
async function updateSettings() {
updateStatus('info', '⚙️ Updating settings...');
try {
const settings = {
url: document.getElementById('configUrl').value,
time: document.getElementById('configTime').value,
retryCount: parseInt(document.getElementById('configRetryCount').value),
sound: true,
priority: 'high'
};
await plugin.updateSettings(settings);
updateStatus('success', `⚙️ Settings updated: ${JSON.stringify(settings, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Settings update failed: ${error.message}`);
}
}
// Get exact alarm status
async function getExactAlarmStatus() {
updateStatus('info', '⏰ Getting exact alarm status...');
try {
const status = await plugin.getExactAlarmStatus();
updateStatus('success', `⏰ Exact alarm status: ${JSON.stringify(status, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Exact alarm check failed: ${error.message}`);
}
}
// Request exact alarm permission
async function requestExactAlarmPermission() {
updateStatus('info', '⏰ Requesting exact alarm permission...');
try {
await plugin.requestExactAlarmPermission();
updateStatus('success', '⏰ Exact alarm permission requested');
} catch (error) {
updateStatus('error', `❌ Exact alarm permission request failed: ${error.message}`);
}
}
// Open exact alarm settings
async function openExactAlarmSettings() {
updateStatus('info', '⚙️ Opening exact alarm settings...');
try {
await plugin.openExactAlarmSettings();
updateStatus('success', '⚙️ Exact alarm settings opened');
} catch (error) {
updateStatus('error', `❌ Open settings failed: ${error.message}`);
}
}
// Request battery optimization exemption
async function requestBatteryOptimizationExemption() {
updateStatus('info', '🔋 Requesting battery optimization exemption...');
try {
await plugin.requestBatteryOptimizationExemption();
updateStatus('success', '🔋 Battery optimization exemption requested');
} catch (error) {
updateStatus('error', `❌ Battery exemption request failed: ${error.message}`);
}
}
// Get reboot recovery status
async function getRebootRecoveryStatus() {
updateStatus('info', '🔄 Getting reboot recovery status...');
try {
const status = await plugin.getRebootRecoveryStatus();
updateStatus('success', `🔄 Reboot recovery status: ${JSON.stringify(status, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Reboot recovery check failed: ${error.message}`);
}
}
// Get rolling window stats
async function getRollingWindowStats() {
updateStatus('info', '📊 Getting rolling window stats...');
try {
const stats = await plugin.getRollingWindowStats();
updateStatus('success', `📊 Rolling window stats: ${JSON.stringify(stats, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Rolling window stats failed: ${error.message}`);
}
}
// Maintain rolling window
async function maintainRollingWindow() {
updateStatus('info', '🔧 Maintaining rolling window...');
try {
await plugin.maintainRollingWindow();
updateStatus('success', '🔧 Rolling window maintenance completed');
} catch (error) {
updateStatus('error', `❌ Rolling window maintenance failed: ${error.message}`);
}
}
// Get content cache
async function getContentCache() {
updateStatus('info', '💾 Getting content cache...');
try {
const cache = await plugin.getContentCache();
updateStatus('success', `💾 Content cache: ${JSON.stringify(cache, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Content cache check failed: ${error.message}`);
}
}
// Clear content cache
async function clearContentCache() {
updateStatus('info', '🗑️ Clearing content cache...');
try {
await plugin.clearContentCache();
updateStatus('success', '🗑️ Content cache cleared');
} catch (error) {
updateStatus('error', `❌ Clear cache failed: ${error.message}`);
}
}
// Get content history
async function getContentHistory() {
updateStatus('info', '📚 Getting content history...');
try {
const history = await plugin.getContentHistory();
updateStatus('success', `📚 Content history: ${JSON.stringify(history, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Content history check failed: ${error.message}`);
}
}
// Get dual schedule status
async function getDualScheduleStatus() {
updateStatus('info', '🔄 Getting dual schedule status...');
try {
const status = await plugin.getDualScheduleStatus();
updateStatus('success', `🔄 Dual schedule status: ${JSON.stringify(status, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Dual schedule check failed: ${error.message}`);
}
}
console.log('🔔 DailyNotification Plugin Test Interface Loaded Successfully!');
</script> </script>
</body> </body>
</html> </html>

Loading…
Cancel
Save