fix(ios): configure method parameter parsing and improve build process
Fix configure() method to read parameters directly from CAPPluginCall instead of expecting nested options object, matching Android implementation. Improve build process to ensure canonical UI is always copied: - iOS build script: Copy www/index.html to test app before build - Android build.gradle: Add copyCanonicalUI task to run before build - Ensures test apps always use latest UI from www/index.html This fixes the issue where configure() was returning 'Configuration options required' error because it expected a nested options object when Capacitor passes parameters directly on the call object.
This commit is contained in:
@@ -52,6 +52,28 @@ dependencies {
|
||||
|
||||
apply from: 'capacitor.build.gradle'
|
||||
|
||||
// Copy canonical UI from www/index.html before each build
|
||||
task copyCanonicalUI(type: Copy) {
|
||||
description = 'Copies canonical UI from www/index.html to test app'
|
||||
group = 'build'
|
||||
|
||||
def repoRoot = project.rootProject.projectDir.parentFile.parentFile
|
||||
def canonicalUI = new File(repoRoot, 'www/index.html')
|
||||
def targetUI = new File(projectDir, 'src/main/assets/public/index.html')
|
||||
|
||||
if (canonicalUI.exists()) {
|
||||
from canonicalUI
|
||||
into targetUI.parentFile
|
||||
rename { 'index.html' }
|
||||
println "✅ Copied canonical UI from ${canonicalUI} to ${targetUI}"
|
||||
} else {
|
||||
println "⚠️ Canonical UI not found at ${canonicalUI}, skipping copy"
|
||||
}
|
||||
}
|
||||
|
||||
// Make copy task run before build
|
||||
preBuild.dependsOn copyCanonicalUI
|
||||
|
||||
try {
|
||||
def servicesJSON = file('google-services.json')
|
||||
if (servicesJSON.text) {
|
||||
|
||||
@@ -17,627 +17,417 @@
|
||||
color: white;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
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 {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
margin: 8px;
|
||||
border-radius: 20px;
|
||||
padding: 15px 30px;
|
||||
margin: 10px;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-block;
|
||||
}
|
||||
.button:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
.status {
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
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);
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
}
|
||||
.input-group input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
border-radius: 10px;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🔔 DailyNotification Plugin Test</h1>
|
||||
<div id="statusCard" class="status" style="margin-bottom: 20px; font-size: 14px;">
|
||||
<strong>Plugin Status</strong><br>
|
||||
<div style="margin-top: 10px;">
|
||||
⚙️ Plugin Settings: <span id="configStatus">Not configured</span><br>
|
||||
🔌 Native Fetcher: <span id="fetcherStatus">Not configured</span><br>
|
||||
🔔 Notifications: <span id="notificationPermStatus">Checking...</span><br>
|
||||
⏰ Exact Alarms: <span id="exactAlarmPermStatus">Checking...</span><br>
|
||||
📢 Channel: <span id="channelStatus">Checking...</span><br>
|
||||
<div id="pluginStatusContent" style="margin-top: 8px;">
|
||||
Loading plugin status...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plugin Status Section -->
|
||||
<div class="section">
|
||||
<h2>📊 Plugin Status</h2>
|
||||
<div class="grid">
|
||||
<button class="button" onclick="checkPluginAvailability()">Check Availability</button>
|
||||
<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>
|
||||
<button class="button" onclick="requestPermissions()">Request Permissions</button>
|
||||
<button class="button" onclick="testNotification()">Test Notification</button>
|
||||
<button class="button" onclick="checkComprehensiveStatus()">Full System Status</button>
|
||||
|
||||
<div id="status" class="status">
|
||||
Ready to test...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
console.log('🔔 DailyNotification Plugin Test Interface Loading...');
|
||||
console.log('Script loading...');
|
||||
console.log('JavaScript is working!');
|
||||
|
||||
// Global variables
|
||||
let plugin = null;
|
||||
let isPluginAvailable = false;
|
||||
// Use real DailyNotification plugin
|
||||
console.log('Using real DailyNotification plugin...');
|
||||
window.DailyNotification = window.Capacitor.Plugins.DailyNotification;
|
||||
|
||||
// Initialize plugin on page load
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
console.log('📱 DOM loaded, initializing plugin...');
|
||||
await initializePlugin();
|
||||
});
|
||||
|
||||
// Initialize the real DailyNotification plugin
|
||||
async function initializePlugin() {
|
||||
// Define functions immediately and attach to window
|
||||
|
||||
function configurePlugin() {
|
||||
console.log('configurePlugin called');
|
||||
const status = document.getElementById('status');
|
||||
const configStatus = document.getElementById('configStatus');
|
||||
const fetcherStatus = document.getElementById('fetcherStatus');
|
||||
|
||||
status.innerHTML = 'Configuring plugin...';
|
||||
status.style.background = 'rgba(255, 255, 0, 0.3)'; // Yellow background
|
||||
|
||||
// Update top status to show configuring
|
||||
configStatus.innerHTML = '⏳ Configuring...';
|
||||
fetcherStatus.innerHTML = '⏳ Waiting...';
|
||||
|
||||
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)');
|
||||
if (!window.DailyNotification) {
|
||||
status.innerHTML = 'DailyNotification plugin not available';
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
configStatus.innerHTML = '❌ Plugin unavailable';
|
||||
fetcherStatus.innerHTML = '❌ Plugin unavailable';
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure plugin settings
|
||||
window.DailyNotification.configure({
|
||||
storage: 'tiered',
|
||||
ttlSeconds: 86400,
|
||||
prefetchLeadMinutes: 60,
|
||||
maxNotificationsPerDay: 3,
|
||||
retentionDays: 7
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Plugin settings configured, now configuring native fetcher...');
|
||||
// Update top status
|
||||
configStatus.innerHTML = '✅ Configured';
|
||||
|
||||
// Configure native fetcher with demo credentials
|
||||
// Note: DemoNativeFetcher uses hardcoded mock data, so this is optional
|
||||
// but demonstrates the API. In production, this would be real credentials.
|
||||
return window.DailyNotification.configureNativeFetcher({
|
||||
apiBaseUrl: 'http://10.0.2.2:3000', // Android emulator → host localhost
|
||||
activeDid: 'did:ethr:0xDEMO1234567890', // Demo DID
|
||||
jwtSecret: 'demo-jwt-secret-for-development-testing'
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Update top status
|
||||
fetcherStatus.innerHTML = '✅ Configured';
|
||||
|
||||
// Update bottom status for user feedback
|
||||
status.innerHTML = 'Plugin configured successfully!';
|
||||
status.style.background = 'rgba(0, 255, 0, 0.3)'; // Green background
|
||||
})
|
||||
.catch(error => {
|
||||
// Update top status with error
|
||||
if (configStatus.innerHTML.includes('Configuring')) {
|
||||
configStatus.innerHTML = '❌ Failed';
|
||||
}
|
||||
if (fetcherStatus.innerHTML.includes('Waiting') || fetcherStatus.innerHTML.includes('Configuring')) {
|
||||
fetcherStatus.innerHTML = '❌ Failed';
|
||||
}
|
||||
|
||||
status.innerHTML = `Configuration failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('❌ Plugin initialization failed:', error);
|
||||
updateStatus('error', `❌ Plugin initialization failed: ${error.message}`);
|
||||
configStatus.innerHTML = '❌ Error';
|
||||
fetcherStatus.innerHTML = '❌ Error';
|
||||
status.innerHTML = `Configuration failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
},
|
||||
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({
|
||||
isActive: true,
|
||||
contentSchedule: { nextRun: Date.now() + 3600000, isEnabled: true },
|
||||
userSchedule: { nextRun: Date.now() + 7200000, isEnabled: true },
|
||||
lastContentFetch: Date.now() - 3600000,
|
||||
lastUserNotification: Date.now() - 7200000
|
||||
});
|
||||
|
||||
function loadPluginStatus() {
|
||||
console.log('loadPluginStatus called');
|
||||
const pluginStatusContent = document.getElementById('pluginStatusContent');
|
||||
const statusCard = document.getElementById('statusCard');
|
||||
|
||||
try {
|
||||
if (!window.DailyNotification) {
|
||||
pluginStatusContent.innerHTML = '❌ DailyNotification plugin not available';
|
||||
statusCard.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
return;
|
||||
}
|
||||
};
|
||||
window.DailyNotification.getNotificationStatus()
|
||||
.then(result => {
|
||||
const nextTime = result.nextNotificationTime ? new Date(result.nextNotificationTime).toLocaleString() : 'None scheduled';
|
||||
const hasSchedules = result.isEnabled || (result.pending && result.pending > 0);
|
||||
const statusIcon = hasSchedules ? '✅' : '⏸️';
|
||||
pluginStatusContent.innerHTML = `${statusIcon} Active Schedules: ${hasSchedules ? 'Yes' : 'No'}<br>
|
||||
📅 Next Notification: ${nextTime}<br>
|
||||
⏳ Pending: ${result.pending || 0}`;
|
||||
statusCard.style.background = hasSchedules ?
|
||||
'rgba(0, 255, 0, 0.15)' : 'rgba(255, 255, 255, 0.1)'; // Green if active, light gray if none
|
||||
})
|
||||
.catch(error => {
|
||||
pluginStatusContent.innerHTML = `⚠️ Status check failed: ${error.message}`;
|
||||
statusCard.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
});
|
||||
} catch (error) {
|
||||
pluginStatusContent.innerHTML = `⚠️ Status check failed: ${error.message}`;
|
||||
statusCard.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function to update status display
|
||||
function updateStatus(type, message) {
|
||||
const statusEl = document.getElementById('status');
|
||||
statusEl.className = `status ${type}`;
|
||||
statusEl.textContent = message;
|
||||
console.log(`[${type.toUpperCase()}] ${message}`);
|
||||
}
|
||||
|
||||
// Plugin availability check
|
||||
async function checkPluginAvailability() {
|
||||
updateStatus('info', '🔍 Checking plugin availability...');
|
||||
|
||||
// Notification test functions
|
||||
function testNotification() {
|
||||
console.log('testNotification called');
|
||||
|
||||
// Quick sanity check - test plugin availability
|
||||
if (window.Capacitor && window.Capacitor.isPluginAvailable) {
|
||||
const isAvailable = window.Capacitor.isPluginAvailable('DailyNotification');
|
||||
console.log('is plugin available?', isAvailable);
|
||||
}
|
||||
|
||||
const status = document.getElementById('status');
|
||||
status.innerHTML = 'Testing plugin connection...';
|
||||
status.style.background = 'rgba(255, 255, 0, 0.3)'; // Yellow background
|
||||
|
||||
try {
|
||||
if (plugin) {
|
||||
updateStatus('success', `✅ Plugin available: ${isPluginAvailable ? 'Real plugin' : 'Mock plugin'}`);
|
||||
} else {
|
||||
updateStatus('error', '❌ Plugin not available');
|
||||
if (!window.DailyNotification) {
|
||||
status.innerHTML = 'DailyNotification plugin not available';
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Availability check failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Get notification status
|
||||
async function getNotificationStatus() {
|
||||
updateStatus('info', '📊 Getting notification status...');
|
||||
try {
|
||||
const status = await plugin.getNotificationStatus();
|
||||
updateStatus('success', `📊 Status: ${JSON.stringify(status, null, 2)}`);
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Status check failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
async function checkPermissions() {
|
||||
updateStatus('info', '🔐 Checking permissions...');
|
||||
try {
|
||||
const permissions = await plugin.checkPermissions();
|
||||
updateStatus('success', `🔐 Permissions: ${JSON.stringify(permissions, null, 2)}`);
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Permission check failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Request permissions
|
||||
async function requestPermissions() {
|
||||
updateStatus('info', '🔐 Requesting permissions...');
|
||||
try {
|
||||
const result = await plugin.requestPermissions();
|
||||
updateStatus('success', `🔐 Permission result: ${JSON.stringify(result, null, 2)}`);
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Permission request failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 timeInput = document.getElementById('notificationTime').value;
|
||||
const [hours, minutes] = timeInput.split(':');
|
||||
|
||||
// Test the notification method directly
|
||||
console.log('Testing notification scheduling...');
|
||||
const now = new Date();
|
||||
const scheduledTime = new Date();
|
||||
scheduledTime.setHours(parseInt(hours), parseInt(minutes), 0, 0);
|
||||
|
||||
// If scheduled time is in the past, schedule for tomorrow
|
||||
if (scheduledTime <= now) {
|
||||
scheduledTime.setDate(scheduledTime.getDate() + 1);
|
||||
}
|
||||
|
||||
// Calculate prefetch time (2 minutes before notification)
|
||||
const prefetchTime = new Date(scheduledTime.getTime() - 120000); // 2 minutes
|
||||
const prefetchTimeReadable = prefetchTime.toLocaleTimeString();
|
||||
const notificationTimeReadable = scheduledTime.toLocaleTimeString();
|
||||
const notificationTime = new Date(now.getTime() + 240000); // 4 minutes from now
|
||||
const prefetchTime = new Date(now.getTime() + 120000); // 2 minutes from now (2 min before notification)
|
||||
const notificationTimeString = notificationTime.getHours().toString().padStart(2, '0') + ':' +
|
||||
notificationTime.getMinutes().toString().padStart(2, '0');
|
||||
const prefetchTimeString = prefetchTime.getHours().toString().padStart(2, '0') + ':' +
|
||||
prefetchTime.getMinutes().toString().padStart(2, '0');
|
||||
const notificationTimeString = scheduledTime.getHours().toString().padStart(2, '0') + ':' +
|
||||
scheduledTime.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
const options = {
|
||||
url: document.getElementById('notificationUrl').value,
|
||||
time: timeInput,
|
||||
title: document.getElementById('notificationTitle').value,
|
||||
body: document.getElementById('notificationBody').value,
|
||||
window.DailyNotification.scheduleDailyNotification({
|
||||
time: notificationTimeString,
|
||||
title: 'Test Notification',
|
||||
body: 'This is a test notification from the DailyNotification plugin!',
|
||||
sound: true,
|
||||
priority: 'high'
|
||||
};
|
||||
await plugin.scheduleDailyNotification(options);
|
||||
updateStatus('success', `✅ Notification scheduled!<br>` +
|
||||
`📥 Prefetch: ${prefetchTimeReadable} (${prefetchTimeString})<br>` +
|
||||
`🔔 Notification: ${notificationTimeReadable} (${notificationTimeString})`);
|
||||
})
|
||||
.then(() => {
|
||||
const prefetchTimeReadable = prefetchTime.toLocaleTimeString();
|
||||
const notificationTimeReadable = notificationTime.toLocaleTimeString();
|
||||
status.innerHTML = '✅ Notification scheduled!<br>' +
|
||||
'📥 Prefetch: ' + prefetchTimeReadable + ' (' + prefetchTimeString + ')<br>' +
|
||||
'🔔 Notification: ' + notificationTimeReadable + ' (' + notificationTimeString + ')';
|
||||
status.style.background = 'rgba(0, 255, 0, 0.3)'; // Green background
|
||||
// Refresh plugin status display
|
||||
setTimeout(() => loadPluginStatus(), 500);
|
||||
})
|
||||
.catch(error => {
|
||||
// Check if this is an exact alarm permission error
|
||||
if (error.code === 'EXACT_ALARM_PERMISSION_REQUIRED' ||
|
||||
error.message.includes('Exact alarm permission') ||
|
||||
error.message.includes('Alarms & reminders')) {
|
||||
status.innerHTML = '⚠️ Exact Alarm Permission Required<br><br>' +
|
||||
'Settings opened automatically.<br>' +
|
||||
'Please enable "Allow exact alarms" and return to try again.';
|
||||
status.style.background = 'rgba(255, 165, 0, 0.3)'; // Orange background
|
||||
} else {
|
||||
status.innerHTML = `❌ Notification failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Scheduling failed: ${error.message}`);
|
||||
status.innerHTML = `Notification test failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all notifications
|
||||
async function cancelAllNotifications() {
|
||||
updateStatus('info', '❌ Cancelling all notifications...');
|
||||
|
||||
|
||||
// Permission management functions
|
||||
function requestPermissions() {
|
||||
console.log('requestPermissions called');
|
||||
const status = document.getElementById('status');
|
||||
status.innerHTML = 'Requesting permissions...';
|
||||
status.style.background = 'rgba(255, 255, 0, 0.3)'; // Yellow background
|
||||
|
||||
try {
|
||||
await plugin.cancelAllNotifications();
|
||||
updateStatus('success', '❌ All notifications cancelled');
|
||||
if (!window.DailyNotification) {
|
||||
status.innerHTML = 'DailyNotification plugin not available';
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
return;
|
||||
}
|
||||
|
||||
window.DailyNotification.requestNotificationPermissions()
|
||||
.then(() => {
|
||||
status.innerHTML = 'Permission request completed! Check your device settings if needed.';
|
||||
status.style.background = 'rgba(0, 255, 0, 0.3)'; // Green background
|
||||
|
||||
// Refresh permission and channel status display after request
|
||||
setTimeout(() => {
|
||||
loadPermissionStatus();
|
||||
loadChannelStatus();
|
||||
}, 1000);
|
||||
})
|
||||
.catch(error => {
|
||||
status.innerHTML = `Permission request failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
});
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Cancel failed: ${error.message}`);
|
||||
status.innerHTML = `Permission request failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
}
|
||||
}
|
||||
|
||||
// Get last notification
|
||||
async function getLastNotification() {
|
||||
updateStatus('info', '📱 Getting last notification...');
|
||||
|
||||
function loadChannelStatus() {
|
||||
const channelStatus = document.getElementById('channelStatus');
|
||||
|
||||
try {
|
||||
const notification = await plugin.getLastNotification();
|
||||
updateStatus('success', `📱 Last notification: ${JSON.stringify(notification, null, 2)}`);
|
||||
if (!window.DailyNotification) {
|
||||
channelStatus.innerHTML = '❌ Plugin unavailable';
|
||||
return;
|
||||
}
|
||||
|
||||
window.DailyNotification.isChannelEnabled()
|
||||
.then(result => {
|
||||
const importanceText = getImportanceText(result.importance);
|
||||
if (result.enabled) {
|
||||
channelStatus.innerHTML = `✅ Enabled (${importanceText})`;
|
||||
} else {
|
||||
channelStatus.innerHTML = `❌ Disabled (${importanceText})`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
channelStatus.innerHTML = '⚠️ Error';
|
||||
});
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Get last notification failed: ${error.message}`);
|
||||
channelStatus.innerHTML = '⚠️ Error';
|
||||
}
|
||||
}
|
||||
|
||||
// Configure plugin
|
||||
async function configurePlugin() {
|
||||
updateStatus('info', '⚙️ Configuring plugin...');
|
||||
|
||||
function checkComprehensiveStatus() {
|
||||
const status = document.getElementById('status');
|
||||
status.innerHTML = 'Checking comprehensive status...';
|
||||
status.style.background = 'rgba(255, 255, 0, 0.3)'; // Yellow background
|
||||
|
||||
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)}`);
|
||||
if (!window.DailyNotification) {
|
||||
status.innerHTML = 'DailyNotification plugin not available';
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
return;
|
||||
}
|
||||
|
||||
window.DailyNotification.checkStatus()
|
||||
.then(result => {
|
||||
const canSchedule = result.canScheduleNow;
|
||||
const issues = [];
|
||||
|
||||
if (!result.postNotificationsGranted) {
|
||||
issues.push('POST_NOTIFICATIONS permission');
|
||||
}
|
||||
if (!result.channelEnabled) {
|
||||
issues.push('notification channel disabled');
|
||||
}
|
||||
if (!result.exactAlarmsGranted) {
|
||||
issues.push('exact alarm permission');
|
||||
}
|
||||
|
||||
let statusText = `Status: ${canSchedule ? 'Ready to schedule' : 'Issues found'}`;
|
||||
if (issues.length > 0) {
|
||||
statusText += `\nIssues: ${issues.join(', ')}`;
|
||||
}
|
||||
|
||||
statusText += `\nChannel: ${getImportanceText(result.channelImportance)}`;
|
||||
statusText += `\nChannel ID: ${result.channelId}`;
|
||||
|
||||
status.innerHTML = statusText;
|
||||
status.style.background = canSchedule ? 'rgba(0, 255, 0, 0.3)' : 'rgba(255, 0, 0, 0.3)';
|
||||
})
|
||||
.catch(error => {
|
||||
status.innerHTML = `Status check failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
});
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Configuration failed: ${error.message}`);
|
||||
status.innerHTML = `Status check failed: ${error.message}`;
|
||||
status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background
|
||||
}
|
||||
}
|
||||
|
||||
function getImportanceText(importance) {
|
||||
switch (importance) {
|
||||
case 0: return 'None (blocked)';
|
||||
case 1: return 'Min';
|
||||
case 2: return 'Low';
|
||||
case 3: return 'Default';
|
||||
case 4: return 'High';
|
||||
case 5: return 'Max';
|
||||
default: return `Unknown (${importance})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Attach to window object
|
||||
window.configurePlugin = configurePlugin;
|
||||
window.testNotification = testNotification;
|
||||
window.requestPermissions = requestPermissions;
|
||||
window.checkComprehensiveStatus = checkComprehensiveStatus;
|
||||
|
||||
// Update settings
|
||||
async function updateSettings() {
|
||||
updateStatus('info', '⚙️ Updating settings...');
|
||||
function loadPermissionStatus() {
|
||||
const notificationPermStatus = document.getElementById('notificationPermStatus');
|
||||
const exactAlarmPermStatus = document.getElementById('exactAlarmPermStatus');
|
||||
|
||||
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)}`);
|
||||
if (!window.DailyNotification) {
|
||||
notificationPermStatus.innerHTML = '❌ Plugin unavailable';
|
||||
exactAlarmPermStatus.innerHTML = '❌ Plugin unavailable';
|
||||
return;
|
||||
}
|
||||
|
||||
window.DailyNotification.checkPermissionStatus()
|
||||
.then(result => {
|
||||
notificationPermStatus.innerHTML = result.notificationsEnabled ? '✅ Granted' : '❌ Not granted';
|
||||
exactAlarmPermStatus.innerHTML = result.exactAlarmEnabled ? '✅ Granted' : '❌ Not granted';
|
||||
})
|
||||
.catch(error => {
|
||||
notificationPermStatus.innerHTML = '⚠️ Error';
|
||||
exactAlarmPermStatus.innerHTML = '⚠️ Error';
|
||||
});
|
||||
} catch (error) {
|
||||
updateStatus('error', `❌ Settings update failed: ${error.message}`);
|
||||
notificationPermStatus.innerHTML = '⚠️ Error';
|
||||
exactAlarmPermStatus.innerHTML = '⚠️ Error';
|
||||
}
|
||||
}
|
||||
|
||||
// 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}`);
|
||||
}
|
||||
}
|
||||
// Load plugin status automatically on page load
|
||||
window.addEventListener('load', () => {
|
||||
console.log('Page loaded, loading plugin status...');
|
||||
// Small delay to ensure Capacitor is ready
|
||||
setTimeout(() => {
|
||||
loadPluginStatus();
|
||||
loadPermissionStatus();
|
||||
loadChannelStatus();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// 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!');
|
||||
console.log('Functions attached to window:', {
|
||||
configurePlugin: typeof window.configurePlugin,
|
||||
testNotification: typeof window.testNotification
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user