Skip to content

Commit

Permalink
Android: Notification Fix for Api 33 (#607)
Browse files Browse the repository at this point in the history
  • Loading branch information
scottrules44 authored Aug 22, 2023
1 parent f12fb57 commit 98f60b9
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 27 deletions.
10 changes: 7 additions & 3 deletions platform/android/sdk/AndroidManifest-New.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
android:name="com.ansca.corona.purchasing.StoreActivity"
android:configChanges="keyboardHidden|screenSize|orientation"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
<activity android:name="com.ansca.corona.notifications.OnNotificationReceiverActivity"
android:noHistory="true"
android:excludeFromRecents="true"
android:taskAffinity=""
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:exported="true" />

<provider android:name="com.ansca.corona.storage.FileContentProvider"
android:authorities="${applicationId}.files"
Expand Down Expand Up @@ -76,7 +82,7 @@
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>

<!--Needed for checking for Camera Intent in Android 11 -->
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
Expand All @@ -91,5 +97,3 @@
</intent>
</queries>
</manifest>


Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public final class NotificationServices extends com.ansca.corona.ApplicationCont

/** Stores all notification configurations that have been set up in Corona. */
private static NotificationSettingsCollection<NotificationSettings> sNotificationCollection =
new NotificationSettingsCollection<NotificationSettings>();
new NotificationSettingsCollection<NotificationSettings>();

/**
* Stores a collection of reserved unique notification IDs.
Expand Down Expand Up @@ -377,12 +377,12 @@ private void postAndUpdateCollectionWith(NotificationSettings settings) {
sNotificationCollection.add(settings.clone());
}
else if ((originalSettings instanceof ScheduledNotificationSettings) &&
(settings instanceof ScheduledNotificationSettings)) {
(settings instanceof ScheduledNotificationSettings)) {
// Notification ID exists. Copy the given settings to the existing settings.
((ScheduledNotificationSettings)originalSettings).copyFrom((ScheduledNotificationSettings)settings);
}
else if ((originalSettings instanceof StatusBarNotificationSettings) &&
(settings instanceof StatusBarNotificationSettings)) {
(settings instanceof StatusBarNotificationSettings)) {
// Notification ID exists. Copy the given settings to the existing settings.
((StatusBarNotificationSettings)originalSettings).copyFrom((StatusBarNotificationSettings)settings);
}
Expand All @@ -397,7 +397,7 @@ else if ((originalSettings instanceof StatusBarNotificationSettings) &&
// Raise an event if we're posting a status bar notification and the Corona runtime is running.
if ((settings instanceof StatusBarNotificationSettings)) {
StatusBarNotificationSettings statusBarSettings = (StatusBarNotificationSettings)settings;

for (com.ansca.corona.CoronaRuntime runtime : com.ansca.corona.CoronaRuntimeProvider.getAllCoronaRuntimes()) {
if (runtime.isRunning()) {
runtime.getTaskDispatcher().send(new com.ansca.corona.events.NotificationReceivedTask("active", statusBarSettings));
Expand Down Expand Up @@ -600,7 +600,7 @@ private void loadSettingsTo(NotificationSettingsCollection<NotificationSettings>
// Get the path to the XML file to read notification settings from.
java.io.File filePath = new java.io.File(getApplicationContext().getCacheDir(), ".system");
filePath = new java.io.File(filePath, "NotificationSettings.xml");

// Do not continue if the XML file does not exist.
// This means that there are no active notifications for this class to manage.
if (filePath.exists() == false) {
Expand Down Expand Up @@ -712,7 +712,7 @@ else if ("data".equals(xmlReader.getName())) {
}
}
else if ((xmlReader.getEventType() == org.xmlpull.v1.XmlPullParser.END_TAG) &&
"statusBar".equals(xmlReader.getName())) {
"statusBar".equals(xmlReader.getName())) {
break;
}
}
Expand All @@ -727,7 +727,7 @@ else if ((xmlReader.getEventType() == org.xmlpull.v1.XmlPullParser.END_TAG) &&
return null;
}
}

/**
* Saves the given notification settings to be loaded later.
* <p>
Expand Down Expand Up @@ -872,7 +872,7 @@ private ApiLevel1() {
* Returns null if given a null "context" or "settings" object.
*/
public static Integer getIconResourceId(
android.content.Context context, StatusBarNotificationSettings settings)
android.content.Context context, StatusBarNotificationSettings settings)
{
// Validate.
if ((context == null) || (settings == null)) {
Expand Down Expand Up @@ -903,7 +903,7 @@ public static Integer getIconResourceId(
* Returns null if given a null "context" or "settings" object.
*/
public static android.app.Notification createNotificationFrom(
android.content.Context context, StatusBarNotificationSettings settings)
android.content.Context context, StatusBarNotificationSettings settings)
{
Integer iconResourceId = ApiLevel1.getIconResourceId(context, settings);
if (iconResourceId == null) {
Expand All @@ -924,10 +924,10 @@ public static android.app.Notification createNotificationFrom(
// context, settings.getContentTitle(),
// settings.getContentText(),
// android.app.PendingIntent.getBroadcast(context, 0, intent, 0));
java.lang.reflect.Method setLatestEventInfoMethod = notification.getClass().getMethod("setLatestEventInfo",
android.content.Context.class, CharSequence.class, CharSequence.class, android.app.PendingIntent.class);
setLatestEventInfoMethod.invoke(notification, context, settings.getContentTitle(),
settings.getContentText(), android.app.PendingIntent.getBroadcast(context, 0, intent, 0));
java.lang.reflect.Method setLatestEventInfoMethod = notification.getClass().getMethod("setLatestEventInfo",
android.content.Context.class, CharSequence.class, CharSequence.class, android.app.PendingIntent.class);
setLatestEventInfoMethod.invoke(notification, context, settings.getContentTitle(),
settings.getContentText(), android.app.PendingIntent.getBroadcast(context, 0, intent, 0));
} catch (Exception e) {
// TODO: Print some warning to the developer telling them to use the ApiLevel16 class if they get here.
return null;
Expand Down Expand Up @@ -978,7 +978,7 @@ private ApiLevel11() {
* Returns null if given a null "context" or "settings" object.
*/
public static android.app.Notification.Builder createNotificationBuilderFrom(
android.content.Context context, StatusBarNotificationSettings settings)
android.content.Context context, StatusBarNotificationSettings settings)
{

Integer iconResourceId = ApiLevel1.getIconResourceId(context, settings);
Expand Down Expand Up @@ -1006,12 +1006,12 @@ public static android.app.Notification.Builder createNotificationBuilderFrom(

// Set an intent to be invoked when the notification has been tapped.
android.content.Intent intent;
intent = StatusBarBroadcastReceiver.createContentIntentFrom(context, settings);
builder.setContentIntent(android.app.PendingIntent.getBroadcast(context, 0, intent, android.app.PendingIntent.FLAG_IMMUTABLE));
intent = OnNotificationReceiverActivity.createContentIntentFrom(context, settings);
builder.setContentIntent(android.app.PendingIntent.getActivity(context, 0, intent, android.app.PendingIntent.FLAG_IMMUTABLE));

// Set an intent to be invoked when the notification has been cleared/removed.
intent = StatusBarBroadcastReceiver.createDeleteIntentFrom(context, settings);
builder.setDeleteIntent(android.app.PendingIntent.getBroadcast(context, 0, intent, android.app.PendingIntent.FLAG_IMMUTABLE));
builder.setDeleteIntent(android.app.PendingIntent.getActivity(context, 0, intent, android.app.PendingIntent.FLAG_IMMUTABLE));

// Return the notification builder object.
return builder;
Expand All @@ -1028,7 +1028,7 @@ public static android.app.Notification.Builder createNotificationBuilderFrom(
* Returns null if given a null "context" or "settings" object.
*/
public static android.app.Notification createNotificationFrom(
android.content.Context context, StatusBarNotificationSettings settings)
android.content.Context context, StatusBarNotificationSettings settings)
{
// Set up a notification builder.
android.app.Notification.Builder builder = ApiLevel11.createNotificationBuilderFrom(context, settings);
Expand Down Expand Up @@ -1064,7 +1064,7 @@ private ApiLevel16() {
* Returns null if given a null "context" or "settings" object.
*/
public static android.app.Notification createNotificationFrom(
android.content.Context context, StatusBarNotificationSettings settings)
android.content.Context context, StatusBarNotificationSettings settings)
{
// Set up a notification builder.
android.app.Notification.Builder builder = ApiLevel11.createNotificationBuilderFrom(context, settings);
Expand Down Expand Up @@ -1121,8 +1121,8 @@ private ApiLevel19() { }
* @param pendingIntent The intent to be invoked when the alarm triggers.
*/
public static void alarmManagerSetExact(
android.app.AlarmManager alarmManager,
int type, long triggerAtMilliseconds, android.app.PendingIntent pendingIntent)
android.app.AlarmManager alarmManager,
int type, long triggerAtMilliseconds, android.app.PendingIntent pendingIntent)
{
if (alarmManager != null) {
alarmManager.setExact(type, triggerAtMilliseconds, pendingIntent);
Expand All @@ -1149,12 +1149,12 @@ private ApiLevel23() { }
* @param pendingIntent The intent to be invoked when the alarm triggers.
*/
public static void alarmManagerSetExactAndAllowWhileIdle(
android.app.AlarmManager alarmManager,
int type, long triggerAtMilliseconds, android.app.PendingIntent pendingIntent)
android.app.AlarmManager alarmManager,
int type, long triggerAtMilliseconds, android.app.PendingIntent pendingIntent)
{
if (alarmManager != null) {
alarmManager.setExactAndAllowWhileIdle(type, triggerAtMilliseconds, pendingIntent);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//////////////////////////////////////////////////////////////////////////////
//
// This file is part of the Corona game engine.
// For overview and more information on licensing please refer to README.md
// Home page: https://github.com/coronalabs/corona
// Contact: [email protected]
//
//////////////////////////////////////////////////////////////////////////////


package com.ansca.corona.notifications;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;

public class OnNotificationReceiverActivity extends Activity {
private static final String INTENT_EXTRA_ID_NAME = "id";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleNotification(this, getIntent());
finish();
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleNotification(this, intent);
finish();
}


/**
* Creates a new Intent object that will call this broadcast receiver's onReceive() method with
* information about the given notification.
* <p>
* Intended to be wrapped by a PendingIntent object and given to an Android "Notification" object
* for its touch/tap listener.
* @param context Context object needed to create the Intent object.
* <p>
* Cannot be null or else an exception will be thrown.
* @param settings Provides the notification ID to be applied to the intent so that the broadcast receiver
* can identify which notification was invoked.
* <p>
* Cannot be null or else an exception will be thrown.
* @return Returns a new Intent object for the given notification settings.
*/
public static android.content.Intent createContentIntentFrom(
android.content.Context context, StatusBarNotificationSettings settings)
{
android.content.Intent intent = createDeleteIntentFrom(context, settings);
intent.setAction(android.content.Intent.ACTION_VIEW);
return intent;
}

/**
* Creates a new Intent object that will call this broadcast receiver's onReceive() method with
* information about the given notification.
* <p>
* Intended to be wrapped by a PendingIntent object and given to an Android "Notification" object
* for its delete listener.
* @param context Context object needed to create the Intent object.
* <p>
* Cannot be null or else an exception will be thrown.
* @param settings Provides the notification ID to be applied to the intent so that the broadcast receiver
* can identify which notification was invoked.
* <p>
* Cannot be null or else an exception will be thrown.
* @return Returns a new Intent object for the given notification settings.
*/
public static android.content.Intent createDeleteIntentFrom(
android.content.Context context, StatusBarNotificationSettings settings)
{
// Validate.
if ((context == null) || (settings == null)) {
throw new NullPointerException();
}

// Create and return the intent.
android.content.Intent intent = new android.content.Intent(context, OnNotificationReceiverActivity.class);
intent.setData(android.net.Uri.parse("notification://statusbar?id=" + Integer.toString(settings.getId())));
intent.setAction(android.content.Intent.ACTION_DELETE);
intent.putExtra(INTENT_EXTRA_ID_NAME, settings.getId());
return intent;
}
private static void handleNotification(Context context, Intent intent) {
// Validate.
if (intent == null) {
return;
}

// Fetch the notification ID from the intent.
if (intent.hasExtra(INTENT_EXTRA_ID_NAME) == false) {
return;
}
int notificationId = intent.getIntExtra(INTENT_EXTRA_ID_NAME, -1);

// Fetch the notification object by ID.
StatusBarNotificationSettings notificationSettings;
NotificationServices notificationServices = new NotificationServices(context);
notificationSettings = notificationServices.fetchStatusBarNotificationById(notificationId);
if (notificationSettings == null) {
return;
}

// Remove the notification settings from Corona.
// This way the status bar item will not reappear when restarting this application.
notificationServices.removeById(notificationSettings.getId());

// If the notification was tapped on, then display the Corona activity and send a notification event.
if (android.content.Intent.ACTION_VIEW.equals(intent.getAction())) {
String applicationStateName = "inactive";

// Create the notification event.
com.ansca.corona.events.NotificationReceivedTask event = new com.ansca.corona.events.NotificationReceivedTask(
applicationStateName, notificationSettings);

for (com.ansca.corona.CoronaRuntime runtime : com.ansca.corona.CoronaRuntimeProvider.getAllCoronaRuntimes()) {
applicationStateName = runtime.isRunning() ? "active" : "inactive";
event = new com.ansca.corona.events.NotificationReceivedTask(
applicationStateName, notificationSettings);
runtime.getTaskDispatcher().send(event);
}

// Display the Corona activity window if not already visible.
Class<?> activityClass = com.ansca.corona.CoronaActivity.class;
try {
android.content.pm.PackageInfo pi = context.getPackageManager().getPackageInfo(context.getPackageName(), android.content.pm.PackageManager.GET_ACTIVITIES);
for (android.content.pm.ActivityInfo ai : pi.activities) {
try {
Class<?> c = Class.forName(ai.name);
if (com.ansca.corona.CoronaActivity.class.isAssignableFrom(c)) {
activityClass = c;
break;
}
} catch (Throwable ignore) {
}
}
}
catch (Throwable ignore) {
}
intent = new android.content.Intent(context, activityClass);
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.putExtra(com.ansca.corona.events.NotificationReceivedTask.NAME, event.toBundle());
context.startActivity(intent);
}
}
}

0 comments on commit 98f60b9

Please sign in to comment.