Compare commits
2 Commits
rollover-i
...
android-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e25841fe9 | ||
|
|
367325452a |
@@ -12,6 +12,7 @@ package com.timesafari.dailynotification;
|
|||||||
|
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -154,9 +155,15 @@ public class DailyNotificationScheduler {
|
|||||||
cancelNotification(duplicateId);
|
cancelNotification(duplicateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create intent for the notification
|
// CRITICAL FIX: Explicitly set component and package for AlarmManager broadcasts
|
||||||
Intent intent = new Intent(context, DailyNotificationReceiver.class);
|
// AlarmManager requires explicit component matching when delivering broadcasts
|
||||||
intent.setAction(com.timesafari.dailynotification.DailyNotificationConstants.ACTION_NOTIFICATION);
|
ComponentName receiverComponent = new ComponentName(
|
||||||
|
context.getPackageName(),
|
||||||
|
"com.timesafari.dailynotification.DailyNotificationReceiver"
|
||||||
|
);
|
||||||
|
Intent intent = new Intent(com.timesafari.dailynotification.DailyNotificationConstants.ACTION_NOTIFICATION);
|
||||||
|
intent.setComponent(receiverComponent);
|
||||||
|
intent.setPackage(context.getPackageName());
|
||||||
intent.putExtra(com.timesafari.dailynotification.DailyNotificationConstants.EXTRA_NOTIFICATION_ID, content.getId());
|
intent.putExtra(com.timesafari.dailynotification.DailyNotificationConstants.EXTRA_NOTIFICATION_ID, content.getId());
|
||||||
|
|
||||||
// Check if this is a static reminder
|
// Check if this is a static reminder
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.app.NotificationChannel
|
|||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -146,8 +147,15 @@ class NotifyReceiver : BroadcastReceiver() {
|
|||||||
// This prevents duplicate alarms when multiple scheduling paths race
|
// 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)
|
// Strategy: Check both by scheduleId (stable) and by trigger time (catches different scheduleIds for same time)
|
||||||
val requestCode = getRequestCode(stableScheduleId)
|
val requestCode = getRequestCode(stableScheduleId)
|
||||||
val checkIntent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
// CRITICAL FIX: Explicitly set component and package for AlarmManager broadcasts
|
||||||
action = "com.timesafari.daily.NOTIFICATION"
|
// 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)
|
||||||
|
setPackage(context.packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check 1: Same scheduleId (stable requestCode) - most reliable
|
// Check 1: Same scheduleId (stable requestCode) - most reliable
|
||||||
@@ -269,12 +277,21 @@ class NotifyReceiver : BroadcastReceiver() {
|
|||||||
Log.w(TAG, "Failed to store notification content in database, continuing with alarm scheduling", e)
|
Log.w(TAG, "Failed to store notification content in database, continuing with alarm scheduling", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIX: Use DailyNotificationReceiver (registered in manifest) instead of NotifyReceiver
|
// CRITICAL FIX: Explicitly set component and package for AlarmManager broadcasts
|
||||||
// FIX: Set action to match manifest registration
|
// AlarmManager requires explicit component matching when delivering broadcasts.
|
||||||
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
// Using Intent(context, Class) constructor may not work reliably with AlarmManager
|
||||||
action = "com.timesafari.daily.NOTIFICATION" // Must match manifest intent-filter action
|
// on all Android versions, especially when the app is in certain states.
|
||||||
putExtra("notification_id", notificationId) // DailyNotificationReceiver expects this extra
|
// Solution: Create Intent with action, then explicitly set component and package.
|
||||||
putExtra("schedule_id", stableScheduleId) // Add stable scheduleId for tracking
|
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)
|
||||||
|
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)
|
||||||
// Also preserve original extras for backward compatibility if needed
|
// Also preserve original extras for backward compatibility if needed
|
||||||
putExtra("title", config.title)
|
putExtra("title", config.title)
|
||||||
putExtra("body", config.body)
|
putExtra("body", config.body)
|
||||||
@@ -282,7 +299,8 @@ class NotifyReceiver : BroadcastReceiver() {
|
|||||||
putExtra("vibration", config.vibration ?: true)
|
putExtra("vibration", config.vibration ?: true)
|
||||||
putExtra("priority", config.priority ?: "normal")
|
putExtra("priority", config.priority ?: "normal")
|
||||||
putExtra("is_static_reminder", isStaticReminder)
|
putExtra("is_static_reminder", isStaticReminder)
|
||||||
putExtra("trigger_time", triggerAtMillis) // Store trigger time for debugging
|
// Store trigger time for debugging
|
||||||
|
putExtra("trigger_time", triggerAtMillis)
|
||||||
if (reminderId != null) {
|
if (reminderId != null) {
|
||||||
putExtra("reminder_id", reminderId)
|
putExtra("reminder_id", reminderId)
|
||||||
}
|
}
|
||||||
@@ -407,9 +425,14 @@ class NotifyReceiver : BroadcastReceiver() {
|
|||||||
*/
|
*/
|
||||||
fun cancelNotification(context: Context, scheduleId: String? = null, triggerAtMillis: Long? = null) {
|
fun cancelNotification(context: Context, scheduleId: String? = null, triggerAtMillis: Long? = null) {
|
||||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
// FIX: Use DailyNotificationReceiver to match what was scheduled
|
// CRITICAL FIX: Use same Intent format as scheduling (explicit component and package)
|
||||||
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
val receiverComponent = ComponentName(
|
||||||
action = "com.timesafari.daily.NOTIFICATION"
|
context.packageName,
|
||||||
|
"com.timesafari.dailynotification.DailyNotificationReceiver"
|
||||||
|
)
|
||||||
|
val intent = Intent("com.timesafari.daily.NOTIFICATION").apply {
|
||||||
|
setComponent(receiverComponent)
|
||||||
|
setPackage(context.packageName)
|
||||||
}
|
}
|
||||||
val requestCode = when {
|
val requestCode = when {
|
||||||
scheduleId != null -> getRequestCode(scheduleId)
|
scheduleId != null -> getRequestCode(scheduleId)
|
||||||
@@ -438,9 +461,14 @@ class NotifyReceiver : BroadcastReceiver() {
|
|||||||
* @return true if alarm is scheduled, false otherwise
|
* @return true if alarm is scheduled, false otherwise
|
||||||
*/
|
*/
|
||||||
fun isAlarmScheduled(context: Context, scheduleId: String? = null, triggerAtMillis: Long? = null): Boolean {
|
fun isAlarmScheduled(context: Context, scheduleId: String? = null, triggerAtMillis: Long? = null): Boolean {
|
||||||
// FIX: Use DailyNotificationReceiver to match what was scheduled
|
// CRITICAL FIX: Use same Intent format as scheduling (explicit component and package)
|
||||||
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
val receiverComponent = ComponentName(
|
||||||
action = "com.timesafari.daily.NOTIFICATION"
|
context.packageName,
|
||||||
|
"com.timesafari.dailynotification.DailyNotificationReceiver"
|
||||||
|
)
|
||||||
|
val intent = Intent("com.timesafari.daily.NOTIFICATION").apply {
|
||||||
|
setComponent(receiverComponent)
|
||||||
|
setPackage(context.packageName)
|
||||||
}
|
}
|
||||||
val requestCode = when {
|
val requestCode = when {
|
||||||
scheduleId != null -> getRequestCode(scheduleId)
|
scheduleId != null -> getRequestCode(scheduleId)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.timesafari.dailynotification
|
package com.timesafari.dailynotification
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
@@ -442,8 +443,14 @@ class ReactivationManager(private val context: Context) {
|
|||||||
return try {
|
return try {
|
||||||
// Check if any PendingIntent for our receiver exists
|
// Check if any PendingIntent for our receiver exists
|
||||||
// This is more reliable than nextAlarmClock
|
// This is more reliable than nextAlarmClock
|
||||||
val intent = Intent(context, NotifyReceiver::class.java).apply {
|
// CRITICAL FIX: Use DailyNotificationReceiver with explicit component/package
|
||||||
action = "com.timesafari.daily.NOTIFICATION"
|
val receiverComponent = ComponentName(
|
||||||
|
context.packageName,
|
||||||
|
"com.timesafari.dailynotification.DailyNotificationReceiver"
|
||||||
|
)
|
||||||
|
val intent = Intent("com.timesafari.daily.NOTIFICATION").apply {
|
||||||
|
setComponent(receiverComponent)
|
||||||
|
setPackage(context.packageName)
|
||||||
}
|
}
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -108,7 +108,8 @@ check_requirements() {
|
|||||||
# Check Android requirements if building Android
|
# Check Android requirements if building Android
|
||||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
||||||
if ! command -v adb &> /dev/null; then
|
if ! command -v adb &> /dev/null; then
|
||||||
log_warn "Android SDK not found (adb not in PATH). Android build will be skipped."
|
log_warn "Android SDK tools not found (adb not in PATH)."
|
||||||
|
log_warn "APK can still be built, but install/launch requires adb."
|
||||||
else
|
else
|
||||||
log_info "✅ Android SDK: $(adb version | head -1)"
|
log_info "✅ Android SDK: $(adb version | head -1)"
|
||||||
fi
|
fi
|
||||||
@@ -238,18 +239,88 @@ if [ "$BUILD_ALL" = true ] || [ "$BUILD_IOS" = true ]; then
|
|||||||
fi
|
fi
|
||||||
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
|
# Android build
|
||||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
||||||
log_step "Building Android app..."
|
log_step "Building Android app..."
|
||||||
|
|
||||||
# Check for Android SDK
|
# Ensure Android SDK is configured
|
||||||
if ! command -v adb &> /dev/null; then
|
if ! find_android_sdk; then
|
||||||
log_warn "adb not found. Android SDK may not be installed."
|
log_error "Cannot build Android app without SDK location"
|
||||||
log_warn "Skipping Android build. Install Android SDK to build Android."
|
exit 1
|
||||||
else
|
fi
|
||||||
|
|
||||||
cd "$PROJECT_DIR/android"
|
cd "$PROJECT_DIR/android"
|
||||||
|
|
||||||
# Build APK
|
# Build APK (Gradle doesn't require adb for building)
|
||||||
if ./gradlew :app:assembleDebug; then
|
if ./gradlew :app:assembleDebug; then
|
||||||
log_info "Android APK built successfully"
|
log_info "Android APK built successfully"
|
||||||
|
|
||||||
@@ -258,8 +329,15 @@ if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
|||||||
if [ -f "$APK_PATH" ]; then
|
if [ -f "$APK_PATH" ]; then
|
||||||
log_info "APK location: $APK_PATH"
|
log_info "APK location: $APK_PATH"
|
||||||
|
|
||||||
# Run on emulator if requested
|
# Run on emulator if requested (requires adb)
|
||||||
if [ "$RUN_ALL" = true ] || [ "$RUN_ANDROID" = true ]; then
|
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
|
||||||
log_step "Installing and launching Android app..."
|
log_step "Installing and launching Android app..."
|
||||||
|
|
||||||
# Check for running emulator
|
# Check for running emulator
|
||||||
@@ -283,6 +361,7 @@ if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
log_error "APK not found at expected location: $APK_PATH"
|
log_error "APK not found at expected location: $APK_PATH"
|
||||||
fi
|
fi
|
||||||
@@ -293,7 +372,6 @@ if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
|||||||
|
|
||||||
cd "$PROJECT_DIR"
|
cd "$PROJECT_DIR"
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
# iOS build
|
# iOS build
|
||||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_IOS" = true ]; then
|
if [ "$BUILD_ALL" = true ] || [ "$BUILD_IOS" = true ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user