4 Commits

Author SHA1 Message Date
Jose Olarte III
0bc75372b5 fix(android): target alarm broadcast to app package so receiver is triggered
Set Intent.setPackage(context.packageName) when creating PendingIntents
for AlarmManager so the broadcast is delivered to DailyNotificationReceiver
on all OEMs. Alarms were firing but the receiver was not invoked when the
component was not explicitly package-targeted.

- NotifyReceiver: setPackage on schedule, cancel, and isAlarmScheduled intents
- ReactivationManager: alarmsExist() use DailyNotificationReceiver + setPackage
- DailyNotificationScheduler: setPackage on ExactAlarmManager path intent
2026-02-05 19:28:30 +08:00
Jose Olarte III
57c7ddb7eb docs(testing): EMULATOR_GUIDE prerequisites, API 35, Apple Silicon; build.sh Android-only sync
EMULATOR_GUIDE.md:
- Add "Checking and Installing Prerequisites" (how to check Node, npm, Java,
  ANDROID_HOME, adb, emulator, AVDs; install steps; reference to
  scripts/check-environment.js)
- Use API 35 and Pixel8_API35 throughout to match project compileSdk/targetSdk
- Document arm64-v8a for Apple Silicon and x86_64 for Intel; add
  troubleshooting for "x86_64 not supported on aarch64 host"
- Bump version to 1.1.0 and last-updated date

test-apps/daily-notification-test/scripts/build.sh:
- When building only Android (--android / --run-android), run
  cap:sync:android instead of cap:sync so iOS pod install is skipped and
  Android build/run succeeds without fixing the iOS Podfile
2026-02-05 18:13:09 +08:00
Jose Olarte III
a3afefeda9 docs(testing): EMULATOR_GUIDE prerequisites and API 35
- Add "Checking and Installing Prerequisites" section:
  - How to check Node, npm, Java, ANDROID_HOME, adb, emulator, AVDs
  - Reference scripts/check-environment.js for partial check
  - Install steps for Node, Java, Android SDK (cmdline-tools only),
    sdkmanager packages, and avdmanager AVD creation
- Align SDK and AVD with project: use API 35 (android-35, build-tools 35.0.0,
  Pixel8_API35) to match compileSdk/targetSdk in variables.gradle
- Bump guide version to 1.1.0 and last-updated date
2026-02-05 17:48:46 +08:00
Jose Olarte III
d0155f0b22 docs(building): update BUILDING.md with iOS prerequisites and clean-build script
Updates BUILDING.md to reflect recent changes in build-native.sh, especially
the Xcode Command Line Tools prerequisite check and the clean-build script.

Problem:
- BUILDING.md didn't mention Xcode Command Line Tools prerequisite
  (recently added to build-native.sh)
- clean-build.sh script exists but wasn't documented
- iOS build troubleshooting lacked Command Line Tools guidance

Changes:
- Add Xcode Command Line Tools to Prerequisites section
  - Document installation command (xcode-select --install)
  - Include verification steps (xcode-select -p, xcodebuild -version)
  - Note that build script automatically checks for these tools
  - Explain that sqlite3 is part of Command Line Tools

- Document clean-build.sh script in Build Scripts section
  - Basic usage: ./scripts/clean-build.sh
  - All options: --all, --clean-gradle-cache, --clean-derived-data,
    --reinstall-node
  - Explain when to use clean builds

- Enhance iOS Native Build Process section
  - Add prerequisite note about Command Line Tools
  - Include troubleshooting commands for pod install issues
  - Reference prerequisites section for details

- Add comprehensive troubleshooting sections
  - Clean Build section at start of Troubleshooting
    - Recommends clean-build as first step for many issues
    - Lists when to use clean builds
  - iOS Build Issues section
    - Command Line Tools configuration errors
    - SQLite/linker issues and pkgx conflicts
    - CocoaPods installation problems
    - All with clear solutions and commands

The documentation now accurately reflects:
- Xcode Command Line Tools as required iOS prerequisite
- clean-build.sh as available build tool
- Complete iOS troubleshooting workflow

Files modified:
- BUILDING.md
2026-01-16 15:38:41 +08:00
7 changed files with 291 additions and 213 deletions

View File

@@ -44,9 +44,11 @@ npx cap run android
## Prerequisites
### Required Software
- **Android Studio** (latest stable version)
- **Android Studio** (latest stable version) - for Android development
- **Java 11+** (for Kotlin compilation)
- **Android SDK** with API level 21+
- **Xcode** (latest stable version) - for iOS development (macOS only)
- **Xcode Command Line Tools** - required for iOS builds (includes `xcodebuild`, `sqlite3`, etc.)
- **Node.js** 16+ (for TypeScript compilation)
- **npm** or **yarn** (for dependency management)
@@ -54,11 +56,35 @@ npx cap run android
- **Gradle Wrapper** (included in project)
- **Kotlin** (configured in build.gradle)
- **TypeScript** (for plugin interface)
- **CocoaPods** - for iOS dependency management
### iOS-Specific Prerequisites
**Xcode Command Line Tools** are required for iOS builds. The build script will verify these are installed:
```bash
# Install Xcode Command Line Tools (if not already installed)
xcode-select --install
```
**Verification:**
```bash
# Check if Command Line Tools are configured
xcode-select -p
# Verify xcodebuild is available
xcodebuild -version
# Verify sqlite3 is available (part of Command Line Tools)
sqlite3 --version
```
**Note:** The build script automatically checks for Command Line Tools and will fail with clear error messages if they're missing.
### System Requirements
- **RAM**: 4GB minimum, 8GB recommended
- **Storage**: 2GB free space
- **OS**: Windows 10+, macOS 10.14+, or Linux
- **OS**: Windows 10+, macOS 10.14+, or Linux (iOS development requires macOS)
## Build Methods
@@ -297,6 +323,8 @@ android/build/reports/tests/test/index.html
### iOS Native Build Process
**Prerequisites:** Ensure Xcode Command Line Tools are installed (see [Prerequisites](#prerequisites) section). The build script will verify this automatically.
#### 1. Navigate to iOS Directory
```bash
cd ios
@@ -307,6 +335,12 @@ cd ios
pod install
```
**Note:** If you encounter issues with `pod install`, ensure Xcode Command Line Tools are properly configured:
```bash
xcode-select --install # Install if missing
xcode-select -p # Verify installation path
```
#### 3. Build Commands
```bash
# Build using Xcode command line
@@ -782,6 +816,13 @@ The project includes several automated build scripts in the `scripts/` directory
./scripts/build-native.sh --platform ios
./scripts/build-native.sh --verbose
# Clean build (removes all build artifacts and caches)
./scripts/clean-build.sh
./scripts/clean-build.sh --all # Also cleans caches and reinstalls dependencies
./scripts/clean-build.sh --clean-gradle-cache # Clean Gradle cache
./scripts/clean-build.sh --clean-derived-data # Clean Xcode DerivedData
./scripts/clean-build.sh --reinstall-node # Reinstall node_modules
# TimeSafari-specific builds
node scripts/build-timesafari.js
@@ -948,6 +989,28 @@ adb logcat | grep DailyNotification
## Troubleshooting
### Clean Build (First Step for Many Issues)
If you encounter persistent build issues, try a clean build first:
```bash
# Clean all build artifacts (recommended first step)
./scripts/clean-build.sh
# Clean everything including caches (for stubborn issues)
./scripts/clean-build.sh --all
# Then rebuild
./scripts/build-native.sh --platform all
```
**When to use clean-build:**
- Build errors that don't make sense
- Dependency conflicts
- Stale build artifacts
- After switching branches
- After updating dependencies
### Common Issues
#### Gradle Sync Failures
@@ -1019,6 +1082,39 @@ File → Project Structure → SDK Location
# Solution: Check Kotlin version in build.gradle
```
#### iOS Build Issues
```bash
# Problem: "Xcode Command Line Tools not configured"
# Error: xcode-select -p fails or xcodebuild not found
# Solution: Install Command Line Tools
xcode-select --install
# Verify installation
xcode-select -p
xcodebuild -version
sqlite3 --version
# Problem: "sqlite3 not found" or linker errors with SQLite
# Solution: Ensure Command Line Tools are properly installed
# The build script checks for this automatically, but if you see linker errors:
xcode-select --install
# Problem: pkgx SQLite conflicts with iOS builds
# Error: Linker errors about libsqlite3.dylib
# Solution: The build script automatically handles this by unsetting problematic
# environment variables. If issues persist:
unset PKGX_DIR DYLD_LIBRARY_PATH LD_LIBRARY_PATH
./scripts/build-native.sh --platform ios
# Problem: "pod install" fails
# Solution: Ensure Command Line Tools are installed
xcode-select --install
# Then reinstall CocoaPods dependencies
cd ios
pod deintegrate
pod install
```
#### Capacitor Integration Issues
```bash
# Problem: Plugin not found in Capacitor app

View File

@@ -1,47 +1,46 @@
fix(build): add SQLite conflict detection and Command Line Tools verification
docs(building): update BUILDING.md with iOS prerequisites and clean-build script
Prevents iOS build failures caused by pkgx SQLite linking conflicts and
ensures Xcode Command Line Tools are properly installed.
Updates BUILDING.md to reflect recent changes in build-native.sh, especially
the Xcode Command Line Tools prerequisite check and the clean-build script.
Problem:
- pkgx installs SQLite built for macOS, causing linker errors when building
for iOS simulator: "linking in dylib built for 'macOS'"
- Missing Command Line Tools cause build failures without clear error messages
- BUILDING.md didn't mention Xcode Command Line Tools prerequisite
(recently added to build-native.sh)
- clean-build.sh script exists but wasn't documented
- iOS build troubleshooting lacked Command Line Tools guidance
Changes:
- Add check_sqlite_conflicts() function
- Detects pkgx SQLite installations in ~/.pkgx
- Warns about macOS dylibs that will cause iOS simulator build failures
- Checks for system SQLite from Command Line Tools
- Validates library paths (DYLD_LIBRARY_PATH, LD_LIBRARY_PATH)
- Add check_command_line_tools() function
- Verifies Xcode Command Line Tools are installed and configured
- Checks for xcodebuild availability
- Verifies sqlite3 is available (part of Command Line Tools)
- Provides clear error messages with installation instructions
- Add Xcode Command Line Tools to Prerequisites section
- Document installation command (xcode-select --install)
- Include verification steps (xcode-select -p, xcodebuild -version)
- Note that build script automatically checks for these tools
- Explain that sqlite3 is part of Command Line Tools
- Enhance pkgx detection in iOS build functions
- Specifically searches for pkgx SQLite dylibs
- Automatically removes pkgx paths from PATH environment variable
- Provides detailed warnings about detected conflicts
- Cleans all problematic environment variables before building
- Document clean-build.sh script in Build Scripts section
- Basic usage: ./scripts/clean-build.sh
- All options: --all, --clean-gradle-cache, --clean-derived-data,
--reinstall-node
- Explain when to use clean builds
- Integrate checks into environment validation
- Runs automatically when building for iOS
- Provides early warnings before build starts
- Fails fast with clear error messages if tools are missing
- Enhance iOS Native Build Process section
- Add prerequisite note about Command Line Tools
- Include troubleshooting commands for pod install issues
- Reference prerequisites section for details
This fixes the linker error:
"ld: building for 'iOS-simulator', but linking in dylib
(/Users/trent/.pkgx/sqlite.org/v3.44.2/lib/libsqlite3.0.dylib)
built for 'macOS'"
- Add comprehensive troubleshooting sections
- Clean Build section at start of Troubleshooting
- Recommends clean-build as first step for many issues
- Lists when to use clean builds
- iOS Build Issues section
- Command Line Tools configuration errors
- SQLite/linker issues and pkgx conflicts
- CocoaPods installation problems
- All with clear solutions and commands
The build script now:
- Detects pkgx SQLite conflicts before building
- Automatically fixes environment variables
- Verifies Command Line Tools are installed
- Provides clear guidance for manual fixes if needed
The documentation now accurately reflects:
- Xcode Command Line Tools as required iOS prerequisite
- clean-build.sh as available build tool
- Complete iOS troubleshooting workflow
Files modified:
- scripts/build-native.sh
- BUILDING.md

View File

@@ -12,7 +12,6 @@ package com.timesafari.dailynotification;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
@@ -155,15 +154,10 @@ public class DailyNotificationScheduler {
cancelNotification(duplicateId);
}
// CRITICAL FIX: Explicitly set component and package for AlarmManager broadcasts
// AlarmManager requires explicit component matching when delivering broadcasts
ComponentName receiverComponent = new ComponentName(
context.getPackageName(),
"com.timesafari.dailynotification.DailyNotificationReceiver"
);
Intent intent = new Intent(com.timesafari.dailynotification.DailyNotificationConstants.ACTION_NOTIFICATION);
intent.setComponent(receiverComponent);
// Create intent for the notification; setPackage ensures AlarmManager delivery on all OEMs
Intent intent = new Intent(context, DailyNotificationReceiver.class);
intent.setPackage(context.getPackageName());
intent.setAction(com.timesafari.dailynotification.DailyNotificationConstants.ACTION_NOTIFICATION);
intent.putExtra(com.timesafari.dailynotification.DailyNotificationConstants.EXTRA_NOTIFICATION_ID, content.getId());
// Check if this is a static reminder

View File

@@ -6,7 +6,6 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
@@ -147,15 +146,9 @@ class NotifyReceiver : BroadcastReceiver() {
// This prevents duplicate alarms when multiple scheduling paths race
// Strategy: Check both by scheduleId (stable) and by trigger time (catches different scheduleIds for same time)
val requestCode = getRequestCode(stableScheduleId)
// CRITICAL FIX: Explicitly set component and package for AlarmManager broadcasts
// AlarmManager requires explicit component matching when delivering broadcasts
val receiverComponent = ComponentName(
context.packageName,
"com.timesafari.dailynotification.DailyNotificationReceiver"
)
val checkIntent = Intent("com.timesafari.daily.NOTIFICATION").apply {
setComponent(receiverComponent)
val checkIntent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
setPackage(context.packageName)
action = "com.timesafari.daily.NOTIFICATION"
}
// Check 1: Same scheduleId (stable requestCode) - most reliable
@@ -277,21 +270,14 @@ class NotifyReceiver : BroadcastReceiver() {
Log.w(TAG, "Failed to store notification content in database, continuing with alarm scheduling", e)
}
// CRITICAL FIX: Explicitly set component and package for AlarmManager broadcasts
// AlarmManager requires explicit component matching when delivering broadcasts.
// Using Intent(context, Class) constructor may not work reliably with AlarmManager
// on all Android versions, especially when the app is in certain states.
// Solution: Create Intent with action, then explicitly set component and package.
val intent = Intent("com.timesafari.daily.NOTIFICATION").apply {
// Explicitly set component to ensure AlarmManager can match it to the receiver
setComponent(receiverComponent)
// Explicitly set package to ensure it matches the app's package (not plugin's)
// FIX: Use DailyNotificationReceiver (registered in manifest) instead of NotifyReceiver
// FIX: Set action to match manifest registration; setPackage() ensures AlarmManager
// delivery reaches this app on all OEMs (see daily-notification-plugin-android-receiver-issue)
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
setPackage(context.packageName)
// Must match manifest intent-filter action
// DailyNotificationReceiver expects this extra
putExtra("notification_id", notificationId)
// Add stable scheduleId for tracking
putExtra("schedule_id", stableScheduleId)
action = "com.timesafari.daily.NOTIFICATION" // Must match manifest intent-filter action
putExtra("notification_id", notificationId) // DailyNotificationReceiver expects this extra
putExtra("schedule_id", stableScheduleId) // Add stable scheduleId for tracking
// Also preserve original extras for backward compatibility if needed
putExtra("title", config.title)
putExtra("body", config.body)
@@ -299,8 +285,7 @@ class NotifyReceiver : BroadcastReceiver() {
putExtra("vibration", config.vibration ?: true)
putExtra("priority", config.priority ?: "normal")
putExtra("is_static_reminder", isStaticReminder)
// Store trigger time for debugging
putExtra("trigger_time", triggerAtMillis)
putExtra("trigger_time", triggerAtMillis) // Store trigger time for debugging
if (reminderId != null) {
putExtra("reminder_id", reminderId)
}
@@ -425,14 +410,10 @@ class NotifyReceiver : BroadcastReceiver() {
*/
fun cancelNotification(context: Context, scheduleId: String? = null, triggerAtMillis: Long? = null) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// CRITICAL FIX: Use same Intent format as scheduling (explicit component and package)
val receiverComponent = ComponentName(
context.packageName,
"com.timesafari.dailynotification.DailyNotificationReceiver"
)
val intent = Intent("com.timesafari.daily.NOTIFICATION").apply {
setComponent(receiverComponent)
// FIX: Use DailyNotificationReceiver to match what was scheduled
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
setPackage(context.packageName)
action = "com.timesafari.daily.NOTIFICATION"
}
val requestCode = when {
scheduleId != null -> getRequestCode(scheduleId)
@@ -461,14 +442,10 @@ class NotifyReceiver : BroadcastReceiver() {
* @return true if alarm is scheduled, false otherwise
*/
fun isAlarmScheduled(context: Context, scheduleId: String? = null, triggerAtMillis: Long? = null): Boolean {
// CRITICAL FIX: Use same Intent format as scheduling (explicit component and package)
val receiverComponent = ComponentName(
context.packageName,
"com.timesafari.dailynotification.DailyNotificationReceiver"
)
val intent = Intent("com.timesafari.daily.NOTIFICATION").apply {
setComponent(receiverComponent)
// FIX: Use DailyNotificationReceiver to match what was scheduled
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
setPackage(context.packageName)
action = "com.timesafari.daily.NOTIFICATION"
}
val requestCode = when {
scheduleId != null -> getRequestCode(scheduleId)

View File

@@ -1,7 +1,6 @@
package com.timesafari.dailynotification
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
@@ -441,16 +440,10 @@ class ReactivationManager(private val context: Context) {
*/
private fun alarmsExist(): Boolean {
return try {
// Check if any PendingIntent for our receiver exists
// This is more reliable than nextAlarmClock
// CRITICAL FIX: Use DailyNotificationReceiver with explicit component/package
val receiverComponent = ComponentName(
context.packageName,
"com.timesafari.dailynotification.DailyNotificationReceiver"
)
val intent = Intent("com.timesafari.daily.NOTIFICATION").apply {
setComponent(receiverComponent)
// Check if any PendingIntent for our receiver exists (must match NotifyReceiver schedule path)
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
setPackage(context.packageName)
action = "com.timesafari.daily.NOTIFICATION"
}
val pendingIntent = PendingIntent.getBroadcast(
context,

View File

@@ -1,8 +1,8 @@
# Running Android App in Standalone Emulator (Without Android Studio)
**Author**: Matthew Raymer
**Last Updated**: 2025-10-12 06:50:00 UTC
**Version**: 1.0.0
**Last Updated**: 2026-02-05
**Version**: 1.1.0
## Overview
@@ -22,6 +22,81 @@ This guide demonstrates how to run the DailyNotification plugin test app in a st
- **Storage**: 2GB free space for emulator
- **OS**: Linux, macOS, or Windows with WSL
## Checking and Installing Prerequisites
### How to check
Run these in a terminal. If a command is missing or a check fails, use the install steps below.
| Requirement | How to check |
|------------------|--------------|
| **Node.js** | `node --version` (v14+ recommended; test app may require 20+) |
| **npm** | `npm --version` |
| **Java** | `java -version` (Java 11+; build scripts expect 11+) |
| **ANDROID_HOME** | `echo $ANDROID_HOME` (must be set to your Android SDK root) |
| **adb** | `adb version` (must be on PATH; usually `$ANDROID_HOME/platform-tools/adb`) |
| **emulator** | `emulator -version` (must be on PATH; usually `$ANDROID_HOME/emulator/emulator`) |
| **At least one AVD** | `emulator -list-avds` (must list at least one device name) |
**Project script:** From the repo root you can run:
```bash
node scripts/check-environment.js
```
This checks Node, npm, Java, and `ANDROID_HOME`. It does **not** check `adb`, `emulator`, or AVDs—verify those manually as above.
### How to install
- **Node.js and npm**
- Install from [nodejs.org](https://nodejs.org/) (LTS), or on macOS: `brew install node`.
- **Java (JDK 11+)**
- macOS: `brew install openjdk@17` and follow the caveats to link (e.g. `sudo ln -sfn $(brew --prefix)/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk`).
- Or install [Eclipse Temurin](https://adoptium.net/) / [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) and ensure `java` and `javac` are on your PATH.
- **Android SDK (without Android Studio)**
1. Download the [Command-line tools only](https://developer.android.com/studio#command-tools) package for your OS.
2. Create an SDK directory, e.g. `mkdir -p ~/android-sdk` and extract the zip so that you have `~/android-sdk/cmdline-tools/latest/` (the `bin` folder with `sdkmanager` and `avdmanager` must be inside `cmdline-tools/latest/`).
3. Set environment variables (add to `~/.zshrc` or `~/.bashrc`):
```bash
export ANDROID_HOME=$HOME/android-sdk
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator
```
4. Install required SDK packages (accept licenses when prompted):
```bash
sdkmanager "platform-tools"
sdkmanager "emulator"
sdkmanager "platforms;android-35"
sdkmanager "build-tools;35.0.0"
```
Install a system image that matches your host CPU:
- **Apple Silicon (M1/M2/M3, aarch64):** `sdkmanager "system-images;android-35;google_apis;arm64-v8a"`
- **Intel Mac / Windows / Linux (x86_64):** `sdkmanager "system-images;android-35;google_apis;x86_64"`
5. Create at least one AVD (use the same image type you installed):
**Apple Silicon:**
```bash
avdmanager create avd -n Pixel8_API35 -k "system-images;android-35;google_apis;arm64-v8a" -d "pixel_8"
```
**Intel / x86_64:**
```bash
avdmanager create avd -n Pixel8_API35 -k "system-images;android-35;google_apis;x86_64" -d "pixel_8"
```
Then start the emulator with: `emulator -avd Pixel8_API35 -no-snapshot-load &` and use `adb wait-for-device` before building/installing the app.
- **Gradle**
The project uses the Gradle Wrapper (`gradlew`) inside the apps `android` directory. No separate Gradle install is needed.
After installing, run the checks again to confirm `adb`, `emulator`, and `emulator -list-avds` work.
## Step-by-Step Process
### 1. Check Available Emulators
@@ -31,21 +106,21 @@ This guide demonstrates how to run the DailyNotification plugin test app in a st
emulator -list-avds
# Example output:
# Pixel8_API34
# Pixel8_API35
```
### 2. Start the Emulator
```bash
# Start emulator in background (recommended)
emulator -avd Pixel8_API34 -no-snapshot-load &
emulator -avd Pixel8_API35 -no-snapshot-load &
# Alternative: Start in foreground
emulator -avd Pixel8_API34
emulator -avd Pixel8_API35
```
**Flags Explained:**
- `-avd Pixel8_API34` - Specifies the AVD to use
- `-avd Pixel8_API35` - Specifies the AVD to use
- `-no-snapshot-load` - Forces fresh boot (recommended for testing)
- `&` - Runs in background (optional)
@@ -141,7 +216,7 @@ adb logcat -c && adb logcat
```bash
# 1. Start emulator
emulator -avd Pixel8_API34 -no-snapshot-load &
emulator -avd Pixel8_API35 -no-snapshot-load &
# 2. Wait for emulator
adb wait-for-device
@@ -211,7 +286,17 @@ ps aux | grep emulator
pkill -f emulator
# Start with verbose logging
emulator -avd Pixel8_API34 -verbose
emulator -avd Pixel8_API35 -verbose
```
#### "x86_64 is not supported by the QEMU2 emulator on aarch64 host"
On Apple Silicon (M1/M2/M3), the emulator cannot run x86_64 system images. Use an ARM64 image and AVD instead:
```bash
sdkmanager "system-images;android-35;google_apis;arm64-v8a"
avdmanager delete avd -n Pixel8_API35 # if you already created an x86_64 AVD
avdmanager create avd -n Pixel8_API35 -k "system-images;android-35;google_apis;arm64-v8a" -d "pixel_8"
emulator -avd Pixel8_API35 -no-snapshot-load
```
#### ADB Connection Issues
@@ -256,13 +341,13 @@ cd android && ./gradlew clean
#### Emulator Performance
```bash
# Start with hardware acceleration
emulator -avd Pixel8_API34 -accel on
emulator -avd Pixel8_API35 -accel on
# Start with specific RAM allocation
emulator -avd Pixel8_API34 -memory 2048
emulator -avd Pixel8_API35 -memory 2048
# Start with GPU acceleration
emulator -avd Pixel8_API34 -gpu host
emulator -avd Pixel8_API35 -gpu host
```
#### Build Performance
@@ -336,7 +421,7 @@ adb shell am start -n com.timesafari.dailynotification/.MainActivity
### Automated Testing
```bash
# CI/CD pipeline
emulator -avd Pixel8_API34 -no-snapshot-load &
emulator -avd Pixel8_API35 -no-snapshot-load &
adb wait-for-device
./scripts/build-native.sh --platform android
cd android && ./gradlew :app:assembleDebug

View File

@@ -108,8 +108,7 @@ check_requirements() {
# Check Android requirements if building Android
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
if ! command -v adb &> /dev/null; then
log_warn "Android SDK tools not found (adb not in PATH)."
log_warn "APK can still be built, but install/launch requires adb."
log_warn "Android SDK not found (adb not in PATH). Android build will be skipped."
else
log_info "✅ Android SDK: $(adb version | head -1)"
fi
@@ -221,11 +220,23 @@ if ! npm run build; then
fi
log_info "Web assets built successfully"
# Step 2: Sync Capacitor
# Step 2: Sync Capacitor (Android-only when building only Android to avoid iOS pod install failure)
log_step "Syncing Capacitor with native projects..."
if ! npm run cap:sync; then
log_error "Capacitor sync failed"
exit 1
if [ "$BUILD_ALL" = true ]; then
if ! npm run cap:sync; then
log_error "Capacitor sync failed"
exit 1
fi
elif [ "$BUILD_ANDROID" = true ]; then
if ! npm run cap:sync:android; then
log_error "Capacitor sync (Android) failed"
exit 1
fi
elif [ "$BUILD_IOS" = true ]; then
if ! npm run cap:sync:ios; then
log_error "Capacitor sync (iOS) failed"
exit 1
fi
fi
log_info "Capacitor sync completed"
@@ -239,105 +250,28 @@ if [ "$BUILD_ALL" = true ] || [ "$BUILD_IOS" = true ]; then
fi
fi
# Find Android SDK location
find_android_sdk() {
local android_dir=""
local local_props="$PROJECT_DIR/android/local.properties"
# Check environment variables first
if [ -n "$ANDROID_HOME" ] && [ -d "$ANDROID_HOME" ]; then
android_dir="$ANDROID_HOME"
log_info "Found Android SDK via ANDROID_HOME: $android_dir"
elif [ -n "$ANDROID_SDK_ROOT" ] && [ -d "$ANDROID_SDK_ROOT" ]; then
android_dir="$ANDROID_SDK_ROOT"
log_info "Found Android SDK via ANDROID_SDK_ROOT: $android_dir"
fi
# Check existing local.properties
if [ -z "$android_dir" ] && [ -f "$local_props" ]; then
# Temporarily disable exit on error for grep (may not find match)
set +e
sdk_line=$(grep "^sdk.dir=" "$local_props" 2>/dev/null)
set -e
if [ -n "$sdk_line" ]; then
android_dir=$(echo "$sdk_line" | cut -d'=' -f2 | sed 's|\\\\|/|g' | sed "s|^~|$HOME|")
if [ -n "$android_dir" ] && [ -d "$android_dir" ]; then
log_info "Found Android SDK in local.properties: $android_dir"
else
android_dir=""
fi
fi
fi
# Try common locations
if [ -z "$android_dir" ]; then
# macOS default location
if [ -d "$HOME/Library/Android/sdk" ]; then
android_dir="$HOME/Library/Android/sdk"
log_info "Found Android SDK in default macOS location: $android_dir"
# Linux default location
elif [ -d "$HOME/Android/Sdk" ]; then
android_dir="$HOME/Android/Sdk"
log_info "Found Android SDK in default Linux location: $android_dir"
fi
fi
# Create/update local.properties if SDK found
if [ -n "$android_dir" ]; then
# Normalize path (convert to forward slashes, expand ~)
android_dir=$(echo "$android_dir" | sed 's|\\\\|/|g' | sed "s|^~|$HOME|")
# Create local.properties with SDK location
mkdir -p "$(dirname "$local_props")"
echo "## This file is automatically generated by build script" > "$local_props"
echo "## Location: $android_dir" >> "$local_props"
echo "sdk.dir=$android_dir" >> "$local_props"
log_info "✅ Configured Android SDK in local.properties"
return 0
else
log_error "Android SDK not found!"
log_error "Please set one of the following:"
log_error " 1. ANDROID_HOME environment variable"
log_error " 2. ANDROID_SDK_ROOT environment variable"
log_error " 3. Create android/local.properties with: sdk.dir=/path/to/android/sdk"
log_error ""
log_error "Common SDK locations:"
log_error " macOS: ~/Library/Android/sdk"
log_error " Linux: ~/Android/Sdk"
return 1
fi
}
# Android build
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
log_step "Building Android app..."
# Ensure Android SDK is configured
if ! find_android_sdk; then
log_error "Cannot build Android app without SDK location"
exit 1
fi
cd "$PROJECT_DIR/android"
# Build APK (Gradle doesn't require adb for building)
if ./gradlew :app:assembleDebug; then
log_info "Android APK built successfully"
# Check for Android SDK
if ! command -v adb &> /dev/null; then
log_warn "adb not found. Android SDK may not be installed."
log_warn "Skipping Android build. Install Android SDK to build Android."
else
cd "$PROJECT_DIR/android"
APK_PATH="$PROJECT_DIR/android/app/build/outputs/apk/debug/app-debug.apk"
if [ -f "$APK_PATH" ]; then
log_info "APK location: $APK_PATH"
# Build APK
if ./gradlew :app:assembleDebug; then
log_info "Android APK built successfully"
# Run on emulator if requested (requires adb)
if [ "$RUN_ALL" = true ] || [ "$RUN_ANDROID" = true ]; then
# Check for Android SDK tools (adb)
if ! command -v adb &> /dev/null; then
log_warn "adb not found in PATH. Cannot install/launch app."
log_warn "APK built successfully, but install/launch requires Android SDK."
log_info "To install manually: adb install -r $APK_PATH"
log_info "Or add Android SDK platform-tools to your PATH."
else
APK_PATH="$PROJECT_DIR/android/app/build/outputs/apk/debug/app-debug.apk"
if [ -f "$APK_PATH" ]; then
log_info "APK location: $APK_PATH"
# Run on emulator if requested
if [ "$RUN_ALL" = true ] || [ "$RUN_ANDROID" = true ]; then
log_step "Installing and launching Android app..."
# Check for running emulator
@@ -361,16 +295,16 @@ if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
fi
fi
fi
else
log_error "APK not found at expected location: $APK_PATH"
fi
else
log_error "APK not found at expected location: $APK_PATH"
log_error "Android build failed"
exit 1
fi
else
log_error "Android build failed"
exit 1
cd "$PROJECT_DIR"
fi
cd "$PROJECT_DIR"
fi
# iOS build