You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

25 KiB

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

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

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:

<!-- 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):

<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>

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

[
  {
    "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

// 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

// 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):

@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:

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:

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

// 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

try {
    const result = await window.DailyNotification.checkStatus();
    // Handle success
} catch (error) {
    // Handle error
    console.error('Status check failed:', error.message);
}

Permission Management Pattern

// 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

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.