Implementation plan hardening: - Document force-stop limitation: system cancels alarms until next explicit launch - Add force-stop test case: no delivery until launch, then rescheduler restores schedules - Make Doze degradation unmistakable: fixed string badge 'Degraded (Doze)' with EVT_DOZE_FALLBACK_TAKEN - Freeze PendingIntent flags rule as Security AC: FLAG_IMMUTABLE unless mutation required Analysis doc clarification: - Add closed vs force-stopped distinction: closing/swiping doesn't affect alarms, force-stopping cancels them These edits harden edge-cases around force-stop behavior and make Doze degradation UI requirements crystal clear for QA testing.
681 lines
25 KiB
Markdown
681 lines
25 KiB
Markdown
# Android App Analysis: DailyNotification Plugin Test App
|
||
|
||
**Author**: Matthew Raymer
|
||
**Date**: 2025-10-24
|
||
**Version**: 1.0.0
|
||
|
||
## Overview
|
||
|
||
This document provides a comprehensive analysis of the `/android/app` portion of the DailyNotification plugin, examining its structure, purpose, and interaction with the `/www` web assets. This analysis is designed to help understand the plugin's test application architecture and provide context for ChatGPT analysis.
|
||
|
||
## Table of Contents
|
||
|
||
- [Architecture Overview](#architecture-overview)
|
||
- [Directory Structure](#directory-structure)
|
||
- [Key Components](#key-components)
|
||
- [Web Asset Integration](#web-asset-integration)
|
||
- [Plugin Integration](#plugin-integration)
|
||
- [Build Configuration](#build-configuration)
|
||
- [Runtime Behavior](#runtime-behavior)
|
||
- [Testing Capabilities](#testing-capabilities)
|
||
|
||
## Architecture Overview
|
||
|
||
### Purpose
|
||
The `/android/app` directory contains a **Capacitor-based Android test application** specifically designed to test and demonstrate the DailyNotification plugin functionality. It serves as:
|
||
|
||
1. **Plugin Testing Environment**: Interactive testing interface for all plugin features
|
||
2. **Development Tool**: Real-time debugging and validation of plugin behavior
|
||
3. **Integration Example**: Reference implementation for plugin integration
|
||
4. **Documentation**: Live demonstration of plugin capabilities
|
||
|
||
### Architecture Pattern
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Android App Container │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ MainActivity (BridgeActivity) │
|
||
│ ├── Capacitor Bridge │
|
||
│ ├── Plugin Discovery │
|
||
│ └── WebView Container │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ Web Assets (/www) │
|
||
│ ├── index.html (Test Interface) │
|
||
│ ├── capacitor.js (Capacitor Runtime) │
|
||
│ └── plugins/ (Plugin JavaScript) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ Native Plugin Integration │
|
||
│ ├── DailyNotificationPlugin.java │
|
||
│ ├── BootReceiver.java │
|
||
│ ├── DailyNotificationReceiver.java │
|
||
│ └── Supporting Classes (34 files) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Directory Structure
|
||
|
||
### Root Android App Structure
|
||
|
||
```
|
||
android/app/
|
||
├── build.gradle # App build configuration
|
||
├── capacitor.build.gradle # Auto-generated Capacitor config
|
||
├── proguard-rules.pro # Code obfuscation rules
|
||
└── src/
|
||
├── main/
|
||
│ ├── AndroidManifest.xml # App permissions and components
|
||
│ ├── assets/ # Web assets (Capacitor www)
|
||
│ │ ├── capacitor.config.json
|
||
│ │ ├── capacitor.plugins.json
|
||
│ │ └── public/ # Web application files
|
||
│ │ ├── index.html # Main test interface
|
||
│ │ ├── capacitor.js # Capacitor runtime
|
||
│ │ ├── capacitor_plugins.js
|
||
│ │ └── plugins/ # Plugin JavaScript files
|
||
│ ├── java/
|
||
│ │ └── com/timesafari/dailynotification/
|
||
│ │ └── MainActivity.java # Capacitor BridgeActivity
|
||
│ └── res/ # Android resources
|
||
│ ├── drawable/ # App icons and images
|
||
│ ├── layout/ # Android layouts
|
||
│ ├── mipmap/ # App launcher icons
|
||
│ ├── values/ # Strings, styles, colors
|
||
│ └── xml/ # Configuration files
|
||
├── androidTest/ # Instrumented tests
|
||
└── test/ # Unit tests
|
||
```
|
||
|
||
## Key Components
|
||
|
||
### 1. MainActivity.java
|
||
|
||
**Purpose**: Entry point extending Capacitor's BridgeActivity
|
||
**Location**: `src/main/java/com/timesafari/dailynotification/MainActivity.java`
|
||
|
||
```java
|
||
public class MainActivity extends BridgeActivity {
|
||
@Override
|
||
protected void onCreate(Bundle savedInstanceState) {
|
||
super.onCreate(savedInstanceState);
|
||
}
|
||
}
|
||
```
|
||
|
||
**Key Features**:
|
||
|
||
- Minimal implementation - Capacitor handles most functionality
|
||
- Extends `BridgeActivity` for automatic plugin discovery
|
||
- Provides WebView container for web assets
|
||
- Handles plugin registration and JavaScript bridge
|
||
|
||
### 2. AndroidManifest.xml
|
||
|
||
**Purpose**: App configuration, permissions, and component declarations
|
||
**Location**: `src/main/AndroidManifest.xml`
|
||
|
||
**Key Declarations**:
|
||
|
||
```xml
|
||
<!-- App Configuration -->
|
||
<application android:name="com.timesafari.dailynotification">
|
||
<activity android:name=".MainActivity" android:exported="true">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.MAIN" />
|
||
<category android:name="android.intent.category.LAUNCHER" />
|
||
</intent-filter>
|
||
</activity>
|
||
|
||
> **Note:** Set `android:name` only if you provide a custom `Application` class; otherwise remove to avoid ClassNotFound at runtime.
|
||
|
||
**Safe default (no custom Application class):**
|
||
```xml
|
||
<application>
|
||
<activity android:name=".MainActivity" android:exported="true">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.MAIN" />
|
||
<category android:name="android.intent.category.LAUNCHER" />
|
||
</intent-filter>
|
||
</activity>
|
||
```
|
||
|
||
<!-- Plugin Components -->
|
||
<!-- Internal receiver: keep non-exported unless intentionally public -->
|
||
<receiver
|
||
android:name="com.timesafari.dailynotification.DailyNotificationReceiver"
|
||
android:enabled="true"
|
||
android:exported="false">
|
||
<intent-filter>
|
||
<action android:name="com.timesafari.daily.NOTIFICATION" />
|
||
</intent-filter>
|
||
</receiver>
|
||
|
||
<receiver
|
||
android:name="com.timesafari.dailynotification.BootReceiver"
|
||
android:enabled="true"
|
||
android:exported="true">
|
||
<intent-filter android:priority="1000">
|
||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||
</intent-filter>
|
||
</receiver>
|
||
|
||
> **Note:** `android:priority` has no practical effect for `BOOT_COMPLETED`; safe to omit.
|
||
|
||
**Minimal example (recommended):**
|
||
```xml
|
||
<receiver
|
||
android:name="com.timesafari.dailynotification.BootReceiver"
|
||
android:enabled="true"
|
||
android:exported="true">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||
</intent-filter>
|
||
</receiver>
|
||
```
|
||
</application>
|
||
|
||
<!-- Required Permissions -->
|
||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||
<uses-permission android:name="android.permission.INTERNET" />
|
||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||
|
||
> **Tip:** `WAKE_LOCK` is typically unnecessary with AlarmManager/WorkManager; remove unless you explicitly acquire/release your own wakelocks.
|
||
> **Note:** `POST_NOTIFICATIONS` is required **on Android 13+**; lower API levels ignore it gracefully.
|
||
```
|
||
|
||
**Critical Permissions**:
|
||
|
||
- `POST_NOTIFICATIONS`: Required for Android 13+ notification posting
|
||
- `SCHEDULE_EXACT_ALARM`: Required for precise notification timing
|
||
- `WAKE_LOCK` **not required** unless you explicitly acquire/release your own wakelocks (AlarmManager & WorkManager handle theirs)
|
||
- `INTERNET`: Required for content fetching
|
||
- `RECEIVE_BOOT_COMPLETED`: Required for reboot recovery
|
||
|
||
> **Note:** If you later introduce foreground services, revisit WAKE_LOCK; otherwise keep it out.
|
||
|
||
### 3. Capacitor Configuration Files
|
||
|
||
#### capacitor.config.json
|
||
|
||
**Purpose**: Capacitor runtime configuration
|
||
|
||
```json
|
||
{
|
||
"appId": "com.timesafari.dailynotification",
|
||
"appName": "DailyNotification Test App",
|
||
"webDir": "www",
|
||
"server": {
|
||
"androidScheme": "https"
|
||
},
|
||
"plugins": {
|
||
"DailyNotification": {
|
||
"fetchUrl": "https://api.example.com/daily-content",
|
||
"scheduleTime": "09:00",
|
||
"enableNotifications": true,
|
||
"debugMode": true
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### capacitor.plugins.json
|
||
|
||
**Purpose**: Plugin discovery and registration
|
||
**Note**: Auto-generated on `npx cap sync` - should not be manually edited
|
||
|
||
```json
|
||
[
|
||
{
|
||
"name": "DailyNotification",
|
||
"classpath": "com.timesafari.dailynotification.DailyNotificationPlugin"
|
||
}
|
||
]
|
||
```
|
||
|
||
## Web Asset Integration
|
||
|
||
### /www Directory Structure
|
||
|
||
The `/www` directory (mapped to `assets/public/`) contains the web application that runs inside the Capacitor WebView:
|
||
|
||
> Capacitor builds copy your `webDir` (e.g., `www/`) to `src/main/assets/public/` on Android; the WebView serves from that `public/` folder.
|
||
|
||
```
|
||
assets/public/
|
||
├── index.html # Main test interface (549 lines)
|
||
├── capacitor.js # Capacitor runtime
|
||
├── capacitor_plugins.js # Plugin JavaScript bridge
|
||
|
||
> **Note:** On pure Capacitor builds, the runtime is `capacitor.js`. Only include `cordova.js/cordova_plugins.js` if Cordova-compat is enabled; otherwise remove those references for accuracy.
|
||
└── plugins/ # Plugin JavaScript files
|
||
```
|
||
|
||
### index.html Analysis
|
||
|
||
**Purpose**: Interactive test interface for plugin functionality
|
||
**Size**: 549 lines of HTML, CSS, and JavaScript
|
||
**Features**:
|
||
|
||
#### 1. User Interface
|
||
|
||
- **Modern Design**: Gradient background, responsive layout
|
||
- **Interactive Buttons**: 12 test functions with visual feedback
|
||
- **Status Display**: Real-time feedback with color-coded results
|
||
- **Mobile-Optimized**: Touch-friendly interface
|
||
|
||
#### 2. Test Categories
|
||
|
||
```javascript
|
||
// Plugin Testing
|
||
- testPlugin() // Basic plugin availability
|
||
- configurePlugin() // Plugin configuration
|
||
- checkStatus() // Plugin status check
|
||
|
||
// Notification Testing
|
||
- testNotification() // Immediate notification test
|
||
- scheduleNotification() // Scheduled notification test
|
||
- showReminder() // Daily reminder test
|
||
|
||
// Permission Management
|
||
- checkPermissions() // Permission status check
|
||
- requestPermissions() // Permission request
|
||
- openExactAlarmSettings() // Settings navigation
|
||
|
||
// Channel Management
|
||
- checkChannelStatus() // Notification channel status
|
||
- openChannelSettings() // Channel settings navigation
|
||
- checkComprehensiveStatus() // Complete status check
|
||
```
|
||
|
||
#### 3. Plugin Integration
|
||
|
||
```javascript
|
||
// Plugin Access Pattern
|
||
window.DailyNotification = window.Capacitor.Plugins.DailyNotification;
|
||
|
||
// Example Usage
|
||
window.DailyNotification.scheduleDailyNotification({
|
||
time: "09:00",
|
||
title: "Test Notification",
|
||
body: "This is a test notification!",
|
||
sound: true,
|
||
priority: "high"
|
||
});
|
||
```
|
||
|
||
#### 4. Error Handling
|
||
|
||
- **Visual Feedback**: Color-coded status indicators
|
||
- **Error Messages**: Detailed error reporting
|
||
- **Graceful Degradation**: Fallback behavior for missing features
|
||
|
||
## Plugin Integration
|
||
|
||
### Plugin Discovery Process
|
||
|
||
1. **Capacitor Startup**: Loads `capacitor.plugins.json`
|
||
2. **Plugin Registration**: Discovers `DailyNotificationPlugin` class
|
||
3. **JavaScript Bridge**: Creates `window.Capacitor.Plugins.DailyNotification`
|
||
4. **Method Exposure**: Exposes `@PluginMethod` annotated methods
|
||
|
||
### Plugin Class Structure
|
||
|
||
**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.java`
|
||
|
||
**Key Methods** (from `@PluginMethod` annotations):
|
||
|
||
```java
|
||
@PluginMethod
|
||
public void configure(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void scheduleDailyNotification(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void scheduleDailyReminder(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void getNotificationStatus(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void checkPermissionStatus(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void requestNotificationPermissions(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void checkStatus(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void isChannelEnabled(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void openChannelSettings(PluginCall call) { ... }
|
||
|
||
@PluginMethod
|
||
public void openExactAlarmSettings(PluginCall call) { ... }
|
||
```
|
||
|
||
### Supporting Classes (34 files)
|
||
|
||
The plugin includes a comprehensive set of supporting classes:
|
||
|
||
**Core Components**:
|
||
|
||
- `BootReceiver.java` - Handles system boot events
|
||
- `DailyNotificationReceiver.java` - Handles notification events
|
||
- `DailyNotificationScheduler.java` - Manages notification scheduling
|
||
- `DailyNotificationFetcher.java` - Handles content fetching
|
||
|
||
**Storage & Database**:
|
||
|
||
- `DailyNotificationStorage.java` - Storage abstraction
|
||
- `DailyNotificationStorageRoom.java` - Room database implementation
|
||
- `DailyNotificationDatabase.java` - Database definition
|
||
- `dao/` - Data Access Objects (3 files)
|
||
- `entities/` - Database entities (3 files)
|
||
|
||
**Management & Utilities**:
|
||
|
||
- `PermissionManager.java` - Permission handling
|
||
- `ChannelManager.java` - Notification channel management
|
||
- `DailyNotificationExactAlarmManager.java` - Exact alarm management
|
||
- `DailyNotificationErrorHandler.java` - Error handling
|
||
- `DailyNotificationPerformanceOptimizer.java` - Performance optimization
|
||
|
||
**Workers & Background Tasks**:
|
||
|
||
- `DailyNotificationWorker.java` - Main background worker
|
||
- `DailyNotificationFetchWorker.java` - Content fetching worker
|
||
- `DailyNotificationMaintenanceWorker.java` - Maintenance tasks
|
||
- `DozeFallbackWorker.java` - Doze mode handling
|
||
- `SoftRefetchWorker.java` - Soft refresh handling
|
||
|
||
## Build Configuration
|
||
|
||
### app/build.gradle
|
||
|
||
**Purpose**: App-level build configuration and dependencies
|
||
|
||
**Key Dependencies**:
|
||
|
||
```gradle
|
||
dependencies {
|
||
// Capacitor Core
|
||
implementation project(':capacitor-android')
|
||
implementation project(':plugin') // DailyNotification plugin
|
||
|
||
// AndroidX Libraries
|
||
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
||
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
|
||
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
|
||
|
||
// Plugin-Specific Dependencies
|
||
implementation "androidx.room:room-runtime:2.6.1"
|
||
implementation "androidx.work:work-runtime-ktx:2.9.0"
|
||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
||
|
||
// Cordova compatibility (include ONLY if using Cordova plugins)
|
||
debugImplementation(project(':capacitor-cordova-android-plugins')) { transitive = false }
|
||
releaseImplementation(project(':capacitor-cordova-android-plugins')) { transitive = false }
|
||
|
||
> **Note:** Include `capacitor-cordova-android-plugins` **only** when using Cordova plugins.
|
||
}
|
||
```
|
||
|
||
**Build Configuration**:
|
||
|
||
```gradle
|
||
android {
|
||
namespace "com.timesafari.dailynotification"
|
||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||
|
||
defaultConfig {
|
||
applicationId "com.timesafari.dailynotification"
|
||
minSdkVersion rootProject.ext.minSdkVersion
|
||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||
versionCode 1
|
||
versionName "1.0"
|
||
}
|
||
}
|
||
```
|
||
|
||
### capacitor.build.gradle
|
||
|
||
**Purpose**: Auto-generated Capacitor configuration
|
||
**Note**: Regenerated on each `npx cap sync` - should not be manually edited
|
||
|
||
**Manifest Hygiene (Quick Scan)**
|
||
|
||
- [ ] `<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>`
|
||
- [ ] `<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>` (if you truly need exact)
|
||
- [ ] `<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>`
|
||
- [ ] BootReceiver: `exported="true"` + BOOT_COMPLETED filter
|
||
- [ ] Other receivers exported=false unless needed
|
||
- [ ] No stray `android:permission=` on BootReceiver
|
||
|
||
## Runtime Behavior
|
||
|
||
### App Startup Sequence
|
||
|
||
1. **Android System**: Launches `MainActivity`
|
||
2. **Capacitor Initialization**: Loads Capacitor runtime
|
||
3. **Plugin Discovery**: Scans `capacitor.plugins.json` for plugins
|
||
4. **Plugin Registration**: Instantiates `DailyNotificationPlugin`
|
||
5. **WebView Loading**: Loads `index.html` from assets
|
||
6. **JavaScript Bridge**: Establishes communication between web and native
|
||
7. **Plugin Availability**: `window.Capacitor.Plugins.DailyNotification` becomes available
|
||
|
||
### Plugin Method Execution Flow
|
||
|
||
```
|
||
JavaScript Call
|
||
↓
|
||
Capacitor Bridge
|
||
↓
|
||
Plugin Method (@PluginMethod)
|
||
↓
|
||
Native Implementation
|
||
↓
|
||
Response (JSObject)
|
||
↓
|
||
JavaScript Promise Resolution
|
||
```
|
||
|
||
### Background Processing
|
||
|
||
- **WorkManager**: Handles background content fetching
|
||
- **AlarmManager**: Manages notification scheduling
|
||
- **BootReceiver**: Reschedules notifications after reboot
|
||
- **Doze Mode**: Handles Android's battery optimization
|
||
|
||
> **Closed vs force-stopped:** Closing/swiping the app does not affect alarms or WorkManager. **Force-stopping** from Settings cancels alarms and suppresses receivers until the next launch, after which Boot/rehydration logic can restore future schedules.
|
||
|
||
## Testing Capabilities
|
||
|
||
### Interactive Testing Features
|
||
|
||
The test app provides comprehensive testing capabilities:
|
||
|
||
#### 1. Plugin Availability Testing
|
||
|
||
- **Basic Detection**: Verify plugin is loaded and accessible
|
||
- **Method Availability**: Check if specific methods are callable
|
||
- **Error Handling**: Test error conditions and edge cases
|
||
|
||
#### 2. Notification Testing
|
||
|
||
- **Immediate Notifications**: Test instant notification display
|
||
- **Scheduled Notifications**: Test time-based notification scheduling
|
||
- **Reminder Testing**: Test daily reminder functionality
|
||
- **Content Testing**: Test with different notification content
|
||
|
||
#### 3. Permission Management
|
||
- **Permission Status**: Check current permission state
|
||
- **Permission Requests**: Test permission request flow
|
||
- **Settings Navigation**: Test opening system settings
|
||
- **Permission Validation**: Verify permission changes
|
||
|
||
#### 4. Channel Management
|
||
- **Channel Status**: Check notification channel state
|
||
- **Channel Settings**: Test channel configuration
|
||
- **Importance Levels**: Test different importance settings
|
||
- **Channel Creation**: Test channel creation and management
|
||
|
||
#### 5. Comprehensive Status Checking
|
||
- **Overall Status**: Complete system status check
|
||
- **Issue Detection**: Identify configuration problems
|
||
- **Readiness Check**: Verify system is ready for notifications
|
||
- **Troubleshooting**: Help identify and resolve issues
|
||
|
||
### Debugging Features
|
||
- **Console Logging**: Detailed console output for debugging
|
||
- **Visual Feedback**: Color-coded status indicators
|
||
- **Error Reporting**: Detailed error messages and stack traces
|
||
- **Real-time Updates**: Live status updates during testing
|
||
|
||
## Integration Patterns
|
||
|
||
### Plugin Integration Pattern
|
||
```javascript
|
||
// 1. Check Plugin Availability
|
||
if (!window.DailyNotification) {
|
||
console.error('Plugin not available');
|
||
return;
|
||
}
|
||
|
||
// 2. Call Plugin Method
|
||
window.DailyNotification.scheduleDailyNotification({
|
||
time: "09:00",
|
||
title: "Daily Notification",
|
||
body: "Your daily content is ready!",
|
||
sound: true,
|
||
priority: "high"
|
||
})
|
||
.then(() => {
|
||
console.log('Notification scheduled successfully');
|
||
})
|
||
.catch(error => {
|
||
console.error('Scheduling failed:', error);
|
||
});
|
||
```
|
||
|
||
### Error Handling Pattern
|
||
```javascript
|
||
try {
|
||
const result = await window.DailyNotification.checkStatus();
|
||
// Handle success
|
||
} catch (error) {
|
||
// Handle error
|
||
console.error('Status check failed:', error.message);
|
||
}
|
||
```
|
||
|
||
### Permission Management Pattern
|
||
```javascript
|
||
// Check permissions first
|
||
const permissions = await window.DailyNotification.checkPermissionStatus();
|
||
if (!permissions.allPermissionsGranted) {
|
||
// Request permissions
|
||
await window.DailyNotification.requestNotificationPermissions();
|
||
}
|
||
```
|
||
|
||
## Key Insights
|
||
|
||
### Strengths
|
||
1. **Comprehensive Testing**: Covers all plugin functionality
|
||
2. **Interactive Interface**: Easy-to-use testing interface
|
||
3. **Real-time Feedback**: Immediate visual feedback
|
||
4. **Error Handling**: Robust error handling and reporting
|
||
5. **Permission Management**: Complete permission testing
|
||
6. **Documentation**: Self-documenting through interface
|
||
|
||
### Architecture Benefits
|
||
1. **Separation of Concerns**: Clear separation between web and native
|
||
2. **Plugin Isolation**: Plugin functionality is isolated and testable
|
||
3. **Capacitor Integration**: Leverages Capacitor's plugin system
|
||
4. **Cross-Platform**: Web interface works across platforms
|
||
5. **Maintainable**: Easy to update and maintain
|
||
|
||
### Use Cases
|
||
1. **Plugin Development**: Test new plugin features
|
||
2. **Integration Testing**: Verify plugin integration
|
||
3. **Debugging**: Debug plugin issues
|
||
4. **Documentation**: Demonstrate plugin capabilities
|
||
5. **Quality Assurance**: Validate plugin functionality
|
||
|
||
## Conclusion
|
||
|
||
The `/android/app` portion of the DailyNotification plugin represents a well-architected test application that provides comprehensive testing capabilities for the plugin. It demonstrates best practices for Capacitor plugin integration, including proper permission handling, error management, and user interface design.
|
||
|
||
The integration between the web assets (`/www`) and native Android code through Capacitor's bridge system creates a seamless testing environment that allows developers to validate plugin functionality in real-time while providing an intuitive interface for non-technical users to test and understand the plugin's capabilities.
|
||
|
||
This architecture serves as both a practical testing tool and a reference implementation for integrating the DailyNotification plugin into other applications.
|
||
|
||
## Assumptions & Versions
|
||
|
||
| Topic | Value | Notes |
|
||
|---|---|---|
|
||
| Android min/target SDK | 24 / 35 | Align with `compileSdkVersion`/`targetSdkVersion`. |
|
||
| Capacitor | v5.x | Confirm web asset naming (`capacitor.js` vs Cordova shims). |
|
||
| WorkManager | 2.9.0 | Matches Gradle deps listed. |
|
||
| Room | 2.6.1 | Matches Gradle deps listed. |
|
||
| Exact Alarms | Tiramisu+ | Requires user grant on many OEMs. |
|
||
|
||
## Bridge Surface (Summary)
|
||
|
||
- `scheduleDailyNotification(req: {time, title, body, sound, priority}) -> {success, scheduledAt?, error?}`
|
||
- `checkPermissionStatus() -> {postNotificationsGranted, exactAlarmGranted, batteryOptIgnored, channelEnabled, ...}`
|
||
- `openChannelSettings() -> {opened: boolean}`
|
||
- `openExactAlarmSettings() -> {opened: boolean}`
|
||
- `requestNotificationPermissions() -> {granted: boolean, permissions: {...}}`
|
||
- `getNotificationStatus() -> {isEnabled, isScheduled, nextNotificationTime, ...}`
|
||
|
||
**Status Matrix MUST include:** `postNotificationsGranted`, `exactAlarmGranted`, `channelEnabled`, `batteryOptimizationsIgnored`, `canScheduleNow`.
|
||
|
||
### Exact-Alarm Decision Rule (User-Visible)
|
||
If `SCHEDULE_EXACT_ALARM` is **granted** → schedule with `setExactAndAllowWhileIdle`.
|
||
If **denied or quota-limited** → schedule via WorkManager (exp backoff + jitter) and surface `E_EXACT_ALARM_DENIED` (with "Degraded timing — Doze may delay" hint).
|
||
|
||
> **Exact Alarm note:** `SCHEDULE_EXACT_ALARM` is a **special app-op**, not a runtime permission prompt. Users grant it via Settings; your UI should deep-link there and reflect denial by degrading to WorkManager.
|
||
|
||
## Permission & Settings Truth Table
|
||
|
||
| Symptom | Likely Cause | Action |
|
||
|---|---|---|
|
||
| No notification posts | `POST_NOTIFICATIONS` denied | Call `requestNotificationPermissions()` |
|
||
| Fires late/misses | No exact alarm grant / Doze | `openExactAlarmSettings()` or fallback to WorkManager |
|
||
| Silent notifications | Channel disabled/low importance | `openChannelSettings()` then retest |
|
||
| Battery optimization kills | App not whitelisted | Guide user to battery optimization settings |
|
||
| Boot reschedule fails | `RECEIVE_BOOT_COMPLETED` denied | Check manifest receiver registration |
|
||
|
||
> **Test UI Integration:** Use "Open Channel Settings" and "Open Exact Alarm Settings" buttons in the test interface to resolve channel and exact alarm issues.
|
||
|
||
## Runtime Flow Diagram
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[JavaScript Call] --> B[Capacitor Bridge]
|
||
B --> C[@PluginMethod → Canonical Error]
|
||
C --> D[Use Case Handler → Canonical Error]
|
||
D --> E{Alarm vs WorkManager}
|
||
E -->|Exact Alarm| F[AlarmManager]
|
||
E -->|Fallback| G[WorkManager]
|
||
F --> H[BootReceiver]
|
||
G --> H
|
||
H --> I[NotificationReceiver]
|
||
I --> J[UI Update]
|
||
|
||
%% Error paths
|
||
C -->|Validation Error → Canonical Error| K[Canonical Error]
|
||
D -->|Use-case Error → Canonical Error| K
|
||
K --> L[JavaScript Promise Rejection]
|
||
```
|
||
|
||
## Cordova vs Capacitor Assets – Accuracy Note
|
||
|
||
> **Note:** If using pure Capacitor v5, the web runtime is `capacitor.js`. If Cordova compatibility is enabled, `cordova.js/cordova_plugins.js` will appear; otherwise remove those references here for accuracy.
|