My app won't open from tapping the notification?
I've been trying to get it to work for about an hour, no amount of AI is helping me and no errors are displayed. I am using the official android development program to make an app and after getting the notifications working, I cannot get it to open the app from the notification, what could be the problem?
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wordoftheday">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Word of the Day"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"> <!-- Add this attribute here -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Receiver definition -->
<receiver
android:name=".DailyNotificationReceiver"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wordoftheday">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Word of the Day"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"> <!-- Add this attribute here -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Receiver definition -->
<receiver
android:name=".DailyNotificationReceiver"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
DailyNotificationReceiver.kt
package com.example.wordoftheday
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
import java.io.BufferedReader
import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class DailyNotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val sharedPref = context.getSharedPreferences("WebViewSettings", Context.MODE_PRIVATE)
val currentLevel = sharedPref.getString("word_cefr_level", "A1") ?: "A1"
val (headword, definition) = getWordForToday(context, currentLevel)
showNotification(context, "$currentLevel Word of the Day:\n$headword", definition)
}
private fun getWordForToday(context: Context, level: String): Pair<String, String> {
try {
val prefs = context.getSharedPreferences("WordAppPrefs", Context.MODE_PRIVATE)
val todayStr = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date())
var firstDateStr = prefs.getString("word_app_first_date", null)
if (firstDateStr == null) {
firstDateStr = todayStr
prefs.edit().putString("word_app_first_date", firstDateStr).apply()
}
val format = SimpleDateFormat("yyyy-MM-dd", Locale.US)
val firstDate = format.parse(firstDateStr) ?: Date()
val currentDate = format.parse(todayStr) ?: Date()
// Clean, fixed date math calculation without any stray characters
val diffDays = ((currentDate.time - firstDate.time) / (1000 * 60 * 60 * 24)).toInt()
val dayNumber = Math.max(1, diffDays + 1)
val inputStream = context.assets.open("$level.csv")
val reader = BufferedReader(InputStreamReader(inputStream))
val lines = reader.readLines().drop(1).filter { it.isNotBlank() }
if (lines.isEmpty()) return Pair("Vocabulary", "Check today's new word in the app!")
val wordIndex = (dayNumber - 1) % lines.size
val parsedRow = parseCSVLine(lines[wordIndex])
if (parsedRow.size >= 4) {
return Pair(parsedRow[1], parsedRow[3])
}
} catch (e: Exception) {
e.printStackTrace()
}
return Pair("Word of the Day", "Tap to learn your new word for today!")
}
private fun parseCSVLine(line: String): List<String> {
val row = mutableListOf<String>()
var inQuotes = false
var currentVal = StringBuilder()
for (i in line.indices) {
val char = line[i]
if (char == '"') {
inQuotes = !inQuotes
} else if (char == ',' && !inQuotes) {
row.add(currentVal.toString().trim().removeSurrounding("\""))
currentVal = StringBuilder()
} else {
currentVal.append(char)
}
}
row.add(currentVal.toString().trim().removeSurrounding("\""))
return row
}
private fun showNotification(context: Context, title: String, message: String) {
val channelId = "daily_word_channel"
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId,
"Daily Word Reminders",
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}
// 1. Explicitly point to MainActivity and apply clear/single flags
val resultIntent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
}
// 2. Generate a valid, safe PendingIntent directly from the intent
val pendingIntent = PendingIntent.getActivity(
context,
1001, // Use a distinct request code instead of 0
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// 3. Build and fire the notification
val builder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(android.R.drawable.ic_popup_reminder)
.setContentTitle(title)
.setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent) // Passes the valid intent
.setAutoCancel(true)
notificationManager.notify(1001, builder.build())
}
}package com.example.wordoftheday
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
import java.io.BufferedReader
import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class DailyNotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val sharedPref = context.getSharedPreferences("WebViewSettings", Context.MODE_PRIVATE)
val currentLevel = sharedPref.getString("word_cefr_level", "A1") ?: "A1"
val (headword, definition) = getWordForToday(context, currentLevel)
showNotification(context, "$currentLevel Word of the Day:\n$headword", definition)
}
private fun getWordForToday(context: Context, level: String): Pair<String, String> {
try {
val prefs = context.getSharedPreferences("WordAppPrefs", Context.MODE_PRIVATE)
val todayStr = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date())
var firstDateStr = prefs.getString("word_app_first_date", null)
if (firstDateStr == null) {
firstDateStr = todayStr
prefs.edit().putString("word_app_first_date", firstDateStr).apply()
}
val format = SimpleDateFormat("yyyy-MM-dd", Locale.US)
val firstDate = format.parse(firstDateStr) ?: Date()
val currentDate = format.parse(todayStr) ?: Date()
// Clean, fixed date math calculation without any stray characters
val diffDays = ((currentDate.time - firstDate.time) / (1000 * 60 * 60 * 24)).toInt()
val dayNumber = Math.max(1, diffDays + 1)
val inputStream = context.assets.open("$level.csv")
val reader = BufferedReader(InputStreamReader(inputStream))
val lines = reader.readLines().drop(1).filter { it.isNotBlank() }
if (lines.isEmpty()) return Pair("Vocabulary", "Check today's new word in the app!")
val wordIndex = (dayNumber - 1) % lines.size
val parsedRow = parseCSVLine(lines[wordIndex])
if (parsedRow.size >= 4) {
return Pair(parsedRow[1], parsedRow[3])
}
} catch (e: Exception) {
e.printStackTrace()
}
return Pair("Word of the Day", "Tap to learn your new word for today!")
}
private fun parseCSVLine(line: String): List<String> {
val row = mutableListOf<String>()
var inQuotes = false
var currentVal = StringBuilder()
for (i in line.indices) {
val char = line[i]
if (char == '"') {
inQuotes = !inQuotes
} else if (char == ',' && !inQuotes) {
row.add(currentVal.toString().trim().removeSurrounding("\""))
currentVal = StringBuilder()
} else {
currentVal.append(char)
}
}
row.add(currentVal.toString().trim().removeSurrounding("\""))
return row
}
private fun showNotification(context: Context, title: String, message: String) {
val channelId = "daily_word_channel"
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId,
"Daily Word Reminders",
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}
// 1. Explicitly point to MainActivity and apply clear/single flags
val resultIntent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
}
// 2. Generate a valid, safe PendingIntent directly from the intent
val pendingIntent = PendingIntent.getActivity(
context,
1001, // Use a distinct request code instead of 0
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// 3. Build and fire the notification
val builder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(android.R.drawable.ic_popup_reminder)
.setContentTitle(title)
.setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent) // Passes the valid intent
.setAutoCancel(true)
notificationManager.notify(1001, builder.build())
}