提交
This commit is contained in:
co-authored by
changyunjia
parent
810a087b78
commit
c28ea8ea4a
Vendored
Executable
+80
@@ -0,0 +1,80 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a06b648d726ee0479a00bac712c49f1
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 0
|
||||
Exclude Editor: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Vendored
Executable
+44
@@ -0,0 +1,44 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
}
|
||||
|
||||
def getCompileSdk(unityLib) {
|
||||
def unityCompileSdk = unityLib.compileSdkVersion
|
||||
def version = unityCompileSdk.find('\\d+').toInteger()
|
||||
if (version < 33) {
|
||||
return 33
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
android {
|
||||
// Checking if namespace exists is needed for 2021.3 (AGP 4.0.1)
|
||||
// When 2021.3 is dropped, remove this check and package="com.unity.androidnotifications" from AndroidManifest
|
||||
if (project.android.hasProperty("namespace")) {
|
||||
namespace "com.unity.androidnotifications"
|
||||
}
|
||||
|
||||
def unityLib = project(':unityLibrary').extensions.getByName('android')
|
||||
compileSdkVersion getCompileSdk(unityLib)
|
||||
buildToolsVersion unityLib.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 22
|
||||
consumerProguardFiles "proguard-rules.pro"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api files('../libs/unity-classes.jar')
|
||||
}
|
||||
Vendored
Executable
+3
@@ -0,0 +1,3 @@
|
||||
# Accessed from C# code
|
||||
-keep class com.unity.androidnotifications.UnityNotificationManager { public *; }
|
||||
-keep interface com.unity.androidnotifications.NotificationCallback { *; }
|
||||
Vendored
Executable
+12
@@ -0,0 +1,12 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity.androidnotifications">
|
||||
<application>
|
||||
<receiver android:name="com.unity.androidnotifications.UnityNotificationManager" android:exported="false" />
|
||||
<receiver android:name="com.unity.androidnotifications.UnityNotificationRestartReceiver" android:enabled="false" android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<meta-data android:name="com.unity.androidnotifications.exact_scheduling" android:value="0" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
</manifest>
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
package com.unity.androidnotifications;
|
||||
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_FIRE_TIME;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.TAG_UNITY;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class UnityNotificationBackgroundThread extends Thread {
|
||||
private static abstract class Task {
|
||||
// returns true if notificationIds was modified (needs to be saved)
|
||||
public abstract boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications);
|
||||
}
|
||||
|
||||
private static class ScheduleNotificationTask extends Task {
|
||||
private int notificationId;
|
||||
private Notification.Builder notificationBuilder;
|
||||
private boolean isCustomized;
|
||||
private boolean isNew;
|
||||
|
||||
public ScheduleNotificationTask(int id, Notification.Builder builder, boolean customized, boolean addedNew) {
|
||||
notificationId = id;
|
||||
notificationBuilder = builder;
|
||||
isCustomized = customized;
|
||||
isNew = addedNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
String id = String.valueOf(notificationId);
|
||||
Integer ID = Integer.valueOf(notificationId);
|
||||
boolean didSchedule = false;
|
||||
try {
|
||||
UnityNotificationManager.mUnityNotificationManager.performNotificationScheduling(notificationId, notificationBuilder, isCustomized);
|
||||
didSchedule = true;
|
||||
} finally {
|
||||
// if failed to schedule or replace, remove
|
||||
if (!didSchedule) {
|
||||
notifications.remove(notificationId);
|
||||
manager.cancelPendingNotificationIntent(notificationId);
|
||||
manager.deleteExpiredNotificationIntent(id);
|
||||
}
|
||||
}
|
||||
|
||||
return isNew;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CancelNotificationTask extends Task {
|
||||
private int notificationId;
|
||||
|
||||
public CancelNotificationTask(int id) {
|
||||
notificationId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
manager.cancelPendingNotificationIntent(notificationId);
|
||||
if (notifications.remove(notificationId) != null) {
|
||||
manager.deleteExpiredNotificationIntent(String.valueOf(notificationId));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CancelAllNotificationsTask extends Task {
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
if (notifications.isEmpty())
|
||||
return false;
|
||||
|
||||
Enumeration<Integer> ids = notifications.keys();
|
||||
while (ids.hasMoreElements()) {
|
||||
Integer notificationId = ids.nextElement();
|
||||
manager.cancelPendingNotificationIntent(notificationId);
|
||||
manager.deleteExpiredNotificationIntent(String.valueOf(notificationId));
|
||||
}
|
||||
|
||||
notifications.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class HousekeepingTask extends Task {
|
||||
UnityNotificationBackgroundThread thread;
|
||||
|
||||
public HousekeepingTask(UnityNotificationBackgroundThread th) {
|
||||
thread = th;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
HashSet<String> notificationIds = new HashSet<>();
|
||||
Enumeration<Integer> ids = notifications.keys();
|
||||
while (ids.hasMoreElements()) {
|
||||
notificationIds.add(String.valueOf(ids.nextElement()));
|
||||
}
|
||||
thread.performHousekeeping(notificationIds);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int TASKS_FOR_HOUSEKEEPING = 50;
|
||||
private LinkedTransferQueue<Task> mTasks = new LinkedTransferQueue();
|
||||
private ConcurrentHashMap<Integer, Notification.Builder> mScheduledNotifications;
|
||||
private UnityNotificationManager mManager;
|
||||
private int mTasksSinceHousekeeping = TASKS_FOR_HOUSEKEEPING; // we want hoursekeeping at the start
|
||||
|
||||
public UnityNotificationBackgroundThread(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> scheduledNotifications) {
|
||||
mManager = manager;
|
||||
mScheduledNotifications = scheduledNotifications;
|
||||
// rescheduling after reboot may have loaded, otherwise load here
|
||||
if (mScheduledNotifications.size() == 0)
|
||||
loadNotifications();
|
||||
}
|
||||
|
||||
public void enqueueNotification(int id, Notification.Builder notificationBuilder, boolean customized, boolean addedNew) {
|
||||
mTasks.add(new UnityNotificationBackgroundThread.ScheduleNotificationTask(id, notificationBuilder, customized, addedNew));
|
||||
}
|
||||
|
||||
public void enqueueCancelNotification(int id) {
|
||||
mTasks.add(new CancelNotificationTask(id));
|
||||
}
|
||||
|
||||
public void enqueueCancelAllNotifications() {
|
||||
mTasks.add(new CancelAllNotificationsTask());
|
||||
}
|
||||
|
||||
private void enqueueHousekeeping() {
|
||||
mTasks.add(new HousekeepingTask(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean haveChanges = false;
|
||||
while (true) {
|
||||
try {
|
||||
Task task = mTasks.take();
|
||||
haveChanges |= executeTask(mManager, task, mScheduledNotifications);
|
||||
if (!(task instanceof HousekeepingTask))
|
||||
++mTasksSinceHousekeeping;
|
||||
if (mTasks.size() == 0 && haveChanges) {
|
||||
haveChanges = false;
|
||||
enqueueHousekeeping();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
if (mTasks.isEmpty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean executeTask(UnityNotificationManager manager, Task task, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
try {
|
||||
return task.run(manager, notifications);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Exception executing notification task", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void performHousekeeping(Set<String> notificationIds) {
|
||||
// don't do housekeeping if last task we did was housekeeping (other=1)
|
||||
boolean performHousekeeping = mTasksSinceHousekeeping >= TASKS_FOR_HOUSEKEEPING;
|
||||
mTasksSinceHousekeeping = 0;
|
||||
if (performHousekeeping)
|
||||
mManager.performNotificationHousekeeping(notificationIds);
|
||||
mManager.saveScheduledNotificationIDs(notificationIds);
|
||||
}
|
||||
|
||||
private void loadNotifications() {
|
||||
List<Notification.Builder> notifications = mManager.loadSavedNotifications();
|
||||
if (notifications == null || notifications.size() == 0)
|
||||
return;
|
||||
final long currentTime = Calendar.getInstance().getTime().getTime();
|
||||
boolean needHousekeeping = false;
|
||||
for (Notification.Builder builder : notifications) {
|
||||
Bundle extras = builder.getExtras();
|
||||
int id = extras.getInt(KEY_ID, -1);
|
||||
long fireTime = extras.getLong(KEY_FIRE_TIME, -1);
|
||||
boolean inFuture = fireTime - currentTime > 0;
|
||||
if (inFuture)
|
||||
mScheduledNotifications.put(id, builder);
|
||||
else
|
||||
needHousekeeping = true;
|
||||
}
|
||||
|
||||
if (needHousekeeping)
|
||||
enqueueHousekeeping();
|
||||
}
|
||||
}
|
||||
+1117
File diff suppressed because it is too large
Load Diff
+67
@@ -0,0 +1,67 @@
|
||||
package com.unity.androidnotifications;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_FIRE_TIME;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_REPEAT_INTERVAL;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.TAG_UNITY;
|
||||
|
||||
public class UnityNotificationRestartReceiver extends BroadcastReceiver {
|
||||
private static final long EXPIRATION_TRESHOLD = 600000; // 10 minutes
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent received_intent) {
|
||||
Log.d(TAG_UNITY, "Rescheduling notifications after restart");
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(received_intent.getAction())) {
|
||||
AsyncTask.execute(() -> { rescheduleSavedNotifications(context); });
|
||||
}
|
||||
}
|
||||
|
||||
private static void rescheduleSavedNotifications(Context context) {
|
||||
UnityNotificationManager manager = UnityNotificationManager.getNotificationManagerImpl(context);
|
||||
List<Notification.Builder> saved_notifications = manager.loadSavedNotifications();
|
||||
Date currentDate = Calendar.getInstance().getTime();
|
||||
|
||||
for (Notification.Builder notificationBuilder : saved_notifications) {
|
||||
rescheduleNotification(manager, currentDate, notificationBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean rescheduleNotification(UnityNotificationManager manager, Date currentDate, Notification.Builder notificationBuilder) {
|
||||
try {
|
||||
Bundle extras = notificationBuilder.getExtras();
|
||||
long repeatInterval = extras.getLong(KEY_REPEAT_INTERVAL, 0L);
|
||||
long fireTime = extras.getLong(KEY_FIRE_TIME, 0L);
|
||||
Date fireTimeDate = new Date(fireTime);
|
||||
|
||||
boolean isRepeatable = repeatInterval > 0;
|
||||
|
||||
if (fireTimeDate.after(currentDate) || isRepeatable) {
|
||||
manager.scheduleAlarmWithNotification(notificationBuilder);
|
||||
return true;
|
||||
} else if (currentDate.getTime() - fireTime < EXPIRATION_TRESHOLD) {
|
||||
// notification is in the past, but not by much, send now
|
||||
int id = extras.getInt(KEY_ID);
|
||||
manager.notify(id, notificationBuilder);
|
||||
return true;
|
||||
} else {
|
||||
Log.d(TAG_UNITY, "Notification expired, not rescheduling, ID: " + extras.getInt(KEY_ID, -1));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to reschedule notification", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+652
@@ -0,0 +1,652 @@
|
||||
package com.unity.androidnotifications;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_CHANNEL_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_FIRE_TIME;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_INTENT_DATA;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_LARGE_ICON;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_NOTIFICATION;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_REPEAT_INTERVAL;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_SMALL_ICON;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_SHOW_IN_FOREGROUND;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_LARGE_ICON;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_PICTURE;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_CONTENT_TITLE;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_SUMMARY_TEXT;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_CONTENT_DESCRIPTION;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_SHOW_WHEN_COLLAPSED;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.TAG_UNITY;
|
||||
|
||||
class UnityNotificationUtilities {
|
||||
/*
|
||||
We serialize notifications and save them to shared prefs, so that if app is killed, we can recreate them.
|
||||
The serialized BLOB starts with a four byte magic number descibing serialization type, followed by an integer version.
|
||||
IMPORTANT: IF YOU DO A CHANGE THAT AFFECTS THE LAYOUT, BUMP THE VERSION, AND ENSURE OLD VERSION STILL DESERIALIZES. ADD TEST.
|
||||
In real life app can get updated having old serialized notifications present, so we should be able to deserialize them.
|
||||
*/
|
||||
// magic stands for "Unity Mobile Notifications Notification"
|
||||
static final byte[] UNITY_MAGIC_NUMBER = new byte[] { 'U', 'M', 'N', 'N'};
|
||||
private static final byte[] UNITY_MAGIC_NUMBER_PARCELLED = new byte[] { 'U', 'M', 'N', 'P'};
|
||||
private static final int NOTIFICATION_SERIALIZATION_VERSION = 3;
|
||||
private static final int INTENT_SERIALIZATION_VERSION = 0;
|
||||
|
||||
static final String SAVED_NOTIFICATION_PRIMARY_KEY = "data";
|
||||
static final String SAVED_NOTIFICATION_FALLBACK_KEY = "fallback.data";
|
||||
|
||||
protected static int findResourceIdInContextByName(Context context, String name) {
|
||||
if (name == null)
|
||||
return 0;
|
||||
|
||||
try {
|
||||
Resources res = context.getResources();
|
||||
if (res != null) {
|
||||
int id = res.getIdentifier(name, "mipmap", context.getPackageName());
|
||||
if (id == 0)
|
||||
return res.getIdentifier(name, "drawable", context.getPackageName());
|
||||
else
|
||||
return id;
|
||||
}
|
||||
return 0;
|
||||
} catch (Resources.NotFoundException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Originally we used to serialize a bundle with predefined list of values.
|
||||
After we exposed entire Notification.Builder to users, this is not sufficient anymore.
|
||||
Unfortunately, while Notification itself is Parcelable and can be marshalled to bytes,
|
||||
it's contents are not guaranteed to be (Binder objects).
|
||||
Hence what we try to do here is:
|
||||
- serialize as is if notification is possibly customized by user
|
||||
- otherwise serialize our stuff, since there is nothing more
|
||||
*/
|
||||
protected static void serializeNotification(SharedPreferences prefs, Notification notification, boolean serializeParcel) {
|
||||
try {
|
||||
String serialized;
|
||||
ByteArrayOutputStream data = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(data);
|
||||
if (serializeParcel) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(KEY_NOTIFICATION, notification);
|
||||
if (serializeNotificationParcel(intent, out)) {
|
||||
out.close();
|
||||
byte[] bytes = data.toByteArray();
|
||||
serialized = Base64.encodeToString(bytes, 0, bytes.length, 0);
|
||||
} else {
|
||||
return; // failed
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (serializeNotificationCustom(notification, out)) {
|
||||
out.flush();
|
||||
byte[] bytes = data.toByteArray();
|
||||
serialized = Base64.encodeToString(bytes, 0, bytes.length, 0);
|
||||
} else {
|
||||
return; // failed
|
||||
}
|
||||
}
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit().clear();
|
||||
editor.putString(SAVED_NOTIFICATION_PRIMARY_KEY, serialized);
|
||||
editor.apply();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean serializeNotificationParcel(Intent intent, DataOutputStream out) {
|
||||
try {
|
||||
byte[] bytes = serializeParcelable(intent);
|
||||
if (bytes == null || bytes.length == 0)
|
||||
return false;
|
||||
out.write(UNITY_MAGIC_NUMBER_PARCELLED);
|
||||
out.writeInt(INTENT_SERIALIZATION_VERSION);
|
||||
out.writeInt(bytes.length);
|
||||
out.write(bytes);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification as Parcel", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification as Parcel", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean serializeNotificationCustom(Notification notification, DataOutputStream out) {
|
||||
try {
|
||||
out.write(UNITY_MAGIC_NUMBER);
|
||||
out.writeInt(NOTIFICATION_SERIALIZATION_VERSION);
|
||||
|
||||
// serialize extras
|
||||
boolean showWhen = notification.extras.getBoolean(Notification.EXTRA_SHOW_WHEN, false);
|
||||
|
||||
out.writeInt(notification.extras.getInt(KEY_ID));
|
||||
serializeString(out, notification.extras.getString(Notification.EXTRA_TITLE));
|
||||
serializeString(out, notification.extras.getString(Notification.EXTRA_TEXT));
|
||||
serializeString(out, notification.extras.getString(KEY_SMALL_ICON));
|
||||
serializeString(out, notification.extras.getString(KEY_LARGE_ICON));
|
||||
out.writeLong(notification.extras.getLong(KEY_FIRE_TIME, -1));
|
||||
out.writeLong(notification.extras.getLong(KEY_REPEAT_INTERVAL, -1));
|
||||
serializeString(out, notification.extras.getString(Notification.EXTRA_BIG_TEXT));
|
||||
out.writeBoolean(notification.extras.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false));
|
||||
out.writeBoolean(showWhen);
|
||||
serializeString(out, notification.extras.getString(KEY_INTENT_DATA));
|
||||
out.writeBoolean(notification.extras.getBoolean(KEY_SHOW_IN_FOREGROUND, true));
|
||||
|
||||
String bigPicture = notification.extras.getString(KEY_BIG_PICTURE);
|
||||
serializeString(out, bigPicture);
|
||||
if (bigPicture != null && bigPicture.length() > 0) {
|
||||
// the following only need to be put in if big picture is there
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_LARGE_ICON));
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_CONTENT_TITLE));
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_CONTENT_DESCRIPTION));
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_SUMMARY_TEXT));
|
||||
out.writeBoolean(notification.extras.getBoolean(KEY_BIG_SHOW_WHEN_COLLAPSED, false));
|
||||
}
|
||||
|
||||
serializeString(out, Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? null : notification.getChannelId());
|
||||
Integer color = UnityNotificationManager.getNotificationColor(notification);
|
||||
out.writeBoolean(color != null);
|
||||
if (color != null)
|
||||
out.writeInt(color);
|
||||
out.writeInt(notification.number);
|
||||
out.writeBoolean(0 != (notification.flags & Notification.FLAG_AUTO_CANCEL));
|
||||
serializeString(out, notification.getGroup());
|
||||
out.writeBoolean(0 != (notification.flags & Notification.FLAG_GROUP_SUMMARY));
|
||||
out.writeInt(UnityNotificationManager.getNotificationGroupAlertBehavior(notification));
|
||||
serializeString(out, notification.getSortKey());
|
||||
if (showWhen)
|
||||
out.writeLong(notification.when);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void serializeString(DataOutputStream out, String s) throws IOException {
|
||||
if (s == null || s.length() == 0)
|
||||
out.writeInt(0);
|
||||
else {
|
||||
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
|
||||
out.writeInt(bytes.length);
|
||||
out.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] serializeParcelable(Parcelable obj) {
|
||||
try {
|
||||
Parcel p = Parcel.obtain();
|
||||
Bundle b = new Bundle();
|
||||
b.putParcelable("obj", obj);
|
||||
p.writeParcelable(b, 0);
|
||||
byte[] result = p.marshall();
|
||||
p.recycle();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize Parcelable", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize Parcelable", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static Object deserializeNotification(Context context, SharedPreferences prefs) {
|
||||
String serializedIntentData = prefs.getString(SAVED_NOTIFICATION_PRIMARY_KEY, "");
|
||||
if (null == serializedIntentData || serializedIntentData.length() <= 0)
|
||||
return null;
|
||||
byte[] bytes = Base64.decode(serializedIntentData, 0);
|
||||
Object notification = deserializeNotification(context, bytes);
|
||||
if (notification != null)
|
||||
return notification;
|
||||
serializedIntentData = prefs.getString(SAVED_NOTIFICATION_FALLBACK_KEY, "");
|
||||
if (null == serializedIntentData || serializedIntentData.length() <= 0)
|
||||
return null;
|
||||
bytes = Base64.decode(serializedIntentData, 0);
|
||||
return deserializeNotification(context, bytes);
|
||||
}
|
||||
|
||||
/* See serialization method above for explaination of fallbacks.
|
||||
This one matches it with one additional fallback: support for "old" bundle serialization.
|
||||
*/
|
||||
private static Object deserializeNotification(Context context, byte[] bytes) {
|
||||
ByteArrayInputStream data = new ByteArrayInputStream(bytes);
|
||||
DataInputStream in = new DataInputStream(data);
|
||||
Notification notification = deserializeNotificationParcelable(in);
|
||||
if (notification != null)
|
||||
return notification;
|
||||
data.reset();
|
||||
Notification.Builder builder = deserializeNotificationCustom(context, in);
|
||||
if (builder == null) {
|
||||
builder = deserializedFromOldIntent(context, bytes);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static boolean readAndCheckMagicNumber(DataInputStream in, byte[] magic) {
|
||||
try {
|
||||
boolean magicNumberMatch = true;
|
||||
for (int i = 0; i < magic.length; ++i) {
|
||||
byte b = in.readByte();
|
||||
if (b != magic[i]) {
|
||||
magicNumberMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return magicNumberMatch;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Notification deserializeNotificationParcelable(DataInputStream in) {
|
||||
try {
|
||||
if (!readAndCheckMagicNumber(in, UNITY_MAGIC_NUMBER_PARCELLED))
|
||||
return null;
|
||||
int version = in.readInt();
|
||||
if (version < 0 || version > INTENT_SERIALIZATION_VERSION)
|
||||
return null;
|
||||
Intent intent = deserializeParcelable(in);
|
||||
Notification notification = intent.getParcelableExtra(KEY_NOTIFICATION);
|
||||
return notification;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification intent", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification intent", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Notification.Builder deserializeNotificationCustom(Context context, DataInputStream in) {
|
||||
try {
|
||||
if (!readAndCheckMagicNumber(in, UNITY_MAGIC_NUMBER))
|
||||
return null;
|
||||
int version = in.readInt();
|
||||
if (version < 0 || version > NOTIFICATION_SERIALIZATION_VERSION)
|
||||
return null;
|
||||
|
||||
// deserialize extras
|
||||
int id = 0;
|
||||
String title, text, smallIcon, largeIcon, bigText, intentData;
|
||||
long fireTime, repeatInterval;
|
||||
boolean usesStopWatch, showWhen, showInForeground = true;
|
||||
Bundle extras = null;
|
||||
String bigPicture = null, bigLargeIcon = null, bigContentTitle = null, bigSummary = null, bigContentDesc = null;
|
||||
boolean bigShowWhenCollapsed = false;
|
||||
if (version < 2) {
|
||||
// no longer serialized since v2
|
||||
extras = deserializeParcelable(in);
|
||||
}
|
||||
|
||||
// before v2 it was extras or variables, since 2 always variables
|
||||
if (extras == null) {
|
||||
// extras serialized manually
|
||||
id = in.readInt();
|
||||
title = deserializeString(in);
|
||||
text = deserializeString(in);
|
||||
smallIcon = deserializeString(in);
|
||||
largeIcon = deserializeString(in);
|
||||
fireTime = in.readLong();
|
||||
repeatInterval = in.readLong();
|
||||
bigText = deserializeString(in);
|
||||
usesStopWatch = in.readBoolean();
|
||||
showWhen = in.readBoolean();
|
||||
intentData = deserializeString(in);
|
||||
if (version > 0)
|
||||
showInForeground = in.readBoolean();
|
||||
|
||||
if (version >= 3) {
|
||||
bigPicture = deserializeString(in);
|
||||
if (bigPicture != null && bigPicture.length() > 0) {
|
||||
// the following only need to be put in if big picture is there
|
||||
bigLargeIcon = deserializeString(in);
|
||||
bigContentTitle = deserializeString(in);
|
||||
bigContentDesc = deserializeString(in);
|
||||
bigSummary = deserializeString(in);
|
||||
bigShowWhenCollapsed = in.readBoolean();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
title = extras.getString(Notification.EXTRA_TITLE);
|
||||
text = extras.getString(Notification.EXTRA_TEXT);
|
||||
smallIcon = extras.getString(KEY_SMALL_ICON);
|
||||
largeIcon = extras.getString(KEY_LARGE_ICON);
|
||||
fireTime = extras.getLong(KEY_FIRE_TIME, -1);
|
||||
repeatInterval = extras.getLong(KEY_REPEAT_INTERVAL, -1);
|
||||
bigText = extras.getString(Notification.EXTRA_BIG_TEXT);
|
||||
usesStopWatch = extras.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false);
|
||||
showWhen = extras.getBoolean(Notification.EXTRA_SHOW_WHEN, false);
|
||||
intentData = extras.getString(KEY_INTENT_DATA);
|
||||
}
|
||||
|
||||
String channelId = deserializeString(in);
|
||||
boolean haveColor = in.readBoolean();
|
||||
int color = 0;
|
||||
if (haveColor)
|
||||
color = in.readInt();
|
||||
int number = in.readInt();
|
||||
boolean shouldAutoCancel = in.readBoolean();
|
||||
String group = deserializeString(in);
|
||||
boolean groupSummary = in.readBoolean();
|
||||
int groupAlertBehavior = in.readInt();
|
||||
String sortKey = deserializeString(in);
|
||||
long when = showWhen ? in.readLong() : 0;
|
||||
|
||||
UnityNotificationManager manager = UnityNotificationManager.getNotificationManagerImpl(context);
|
||||
Notification.Builder builder = manager.createNotificationBuilder(channelId);
|
||||
if (extras != null)
|
||||
builder.setExtras(extras);
|
||||
else {
|
||||
builder.getExtras().putInt(KEY_ID, id);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_SMALL_ICON, smallIcon);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_LARGE_ICON, largeIcon);
|
||||
if (fireTime != -1)
|
||||
builder.getExtras().putLong(KEY_FIRE_TIME, fireTime);
|
||||
if (repeatInterval != -1)
|
||||
builder.getExtras().putLong(KEY_REPEAT_INTERVAL, repeatInterval);
|
||||
if (intentData != null)
|
||||
builder.getExtras().putString(KEY_INTENT_DATA, intentData);
|
||||
builder.getExtras().putBoolean(KEY_SHOW_IN_FOREGROUND, showInForeground);
|
||||
}
|
||||
if (title != null)
|
||||
builder.setContentTitle(title);
|
||||
if (text != null)
|
||||
builder.setContentText(text);
|
||||
if (bigText != null)
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(bigText));
|
||||
else if (bigPicture != null)
|
||||
manager.setupBigPictureStyle(builder, bigLargeIcon, bigPicture, bigContentTitle, bigContentDesc, bigSummary, bigShowWhenCollapsed);
|
||||
if (haveColor)
|
||||
UnityNotificationManager.setNotificationColor(builder, color);
|
||||
if (number >= 0)
|
||||
builder.setNumber(number);
|
||||
builder.setAutoCancel(shouldAutoCancel);
|
||||
UnityNotificationManager.setNotificationUsesChronometer(builder, usesStopWatch);
|
||||
if (group != null && group.length() > 0)
|
||||
builder.setGroup(group);
|
||||
builder.setGroupSummary(groupSummary);
|
||||
UnityNotificationManager.setNotificationGroupAlertBehavior(builder, groupAlertBehavior);
|
||||
if (sortKey != null && sortKey.length() > 0)
|
||||
builder.setSortKey(sortKey);
|
||||
if (showWhen) {
|
||||
builder.setShowWhen(true);
|
||||
builder.setWhen(when);
|
||||
}
|
||||
|
||||
return builder;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Notification.Builder deserializedFromOldIntent(Context context, byte[] bytes) {
|
||||
try {
|
||||
Parcel p = Parcel.obtain();
|
||||
p.unmarshall(bytes, 0, bytes.length);
|
||||
p.setDataPosition(0);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.readFromParcel(p);
|
||||
|
||||
int id = bundle.getInt(KEY_ID, -1);
|
||||
String channelId = bundle.getString("channelID");
|
||||
String textTitle = bundle.getString("textTitle");
|
||||
String textContent = bundle.getString("textContent");
|
||||
String smallIcon = bundle.getString("smallIconStr");
|
||||
boolean autoCancel = bundle.getBoolean("autoCancel", false);
|
||||
boolean usesChronometer = bundle.getBoolean("usesChronometer", false);
|
||||
long fireTime = bundle.getLong(KEY_FIRE_TIME, -1);
|
||||
long repeatInterval = bundle.getLong(KEY_REPEAT_INTERVAL, -1);
|
||||
String largeIcon = bundle.getString("largeIconStr");
|
||||
int style = bundle.getInt("style", -1);
|
||||
int color = bundle.getInt("color", 0);
|
||||
int number = bundle.getInt("number", 0);
|
||||
String intentData = bundle.getString(KEY_INTENT_DATA);
|
||||
String group = bundle.getString("group");
|
||||
boolean groupSummary = bundle.getBoolean("groupSummary", false);
|
||||
String sortKey = bundle.getString("sortKey");
|
||||
int groupAlertBehaviour = bundle.getInt("groupAlertBehaviour", -1);
|
||||
boolean showTimestamp = bundle.getBoolean("showTimestamp", false);
|
||||
|
||||
Notification.Builder builder = UnityNotificationManager.getNotificationManagerImpl(context).createNotificationBuilder(channelId);
|
||||
builder.getExtras().putInt(KEY_ID, id);
|
||||
builder.setContentTitle(textTitle);
|
||||
builder.setContentText(textContent);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_SMALL_ICON, smallIcon);
|
||||
builder.setAutoCancel(autoCancel);
|
||||
builder.setUsesChronometer(usesChronometer);
|
||||
builder.getExtras().putLong(KEY_FIRE_TIME, fireTime);
|
||||
builder.getExtras().putLong(KEY_REPEAT_INTERVAL, repeatInterval);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_LARGE_ICON, largeIcon);
|
||||
if (style == 2)
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(textContent));
|
||||
if (color != 0)
|
||||
UnityNotificationManager.setNotificationColor(builder, color);
|
||||
if (number >= 0)
|
||||
builder.setNumber(number);
|
||||
if (intentData != null)
|
||||
builder.getExtras().putString(KEY_INTENT_DATA, intentData);
|
||||
if (null != group && group.length() > 0)
|
||||
builder.setGroup(group);
|
||||
builder.setGroupSummary(groupSummary);
|
||||
if (null != sortKey && sortKey.length() > 0)
|
||||
builder.setSortKey(sortKey);
|
||||
UnityNotificationManager.setNotificationGroupAlertBehavior(builder, groupAlertBehaviour);
|
||||
builder.setShowWhen(showTimestamp);
|
||||
return builder;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize old style notification", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize old style notification", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String deserializeString(DataInputStream in) throws IOException {
|
||||
int length = in.readInt();
|
||||
if (length <= 0)
|
||||
return null;
|
||||
byte[] bytes = new byte[length];
|
||||
int didRead = in.read(bytes);
|
||||
if (didRead != bytes.length)
|
||||
throw new IOException("Insufficient amount of bytes read");
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static <T extends Parcelable> T deserializeParcelable(DataInputStream in) throws IOException {
|
||||
int length = in.readInt();
|
||||
if (length <= 0)
|
||||
return null;
|
||||
byte[] bytes = new byte[length];
|
||||
int didRead = in.read(bytes);
|
||||
if (didRead != bytes.length)
|
||||
throw new IOException("Insufficient amount of bytes read");
|
||||
|
||||
try {
|
||||
Parcel p = Parcel.obtain();
|
||||
p.unmarshall(bytes, 0, bytes.length);
|
||||
p.setDataPosition(0);
|
||||
Bundle b = p.readParcelable(UnityNotificationUtilities.class.getClassLoader());
|
||||
p.recycle();
|
||||
if (b != null) {
|
||||
return b.getParcelable("obj");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize parcelable", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize parcelable", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Returns Activity class to be opened when notification is tapped
|
||||
// Search is done in this order:
|
||||
// * class specified in meta-data key custom_notification_android_activity
|
||||
// * the only enabled activity with name ending in either .UnityPlayerActivity or .UnityPlayerGameActivity
|
||||
// * the only enabled activity in the package
|
||||
protected static Class<?> getOpenAppActivity(Context context) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
|
||||
Bundle bundle = ai.metaData;
|
||||
|
||||
if (bundle.containsKey("custom_notification_android_activity")) {
|
||||
try {
|
||||
return Class.forName(bundle.getString("custom_notification_android_activity"));
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(TAG_UNITY, "Specified activity class for notifications not found: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Log.w(TAG_UNITY, "No custom_notification_android_activity found, attempting to find app activity class");
|
||||
|
||||
ActivityInfo[] aInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES).activities;
|
||||
if (aInfo == null) {
|
||||
Log.e(TAG_UNITY, "Could not get package activities");
|
||||
return null;
|
||||
}
|
||||
|
||||
String activityClassName = null;
|
||||
boolean activityIsUnity = false, activityConflict = false;
|
||||
for (ActivityInfo info : aInfo) {
|
||||
// activity alias not supported
|
||||
if (!info.enabled || info.targetActivity != null)
|
||||
continue;
|
||||
|
||||
boolean candidateIsUnity = isUnityActivity(info.name);
|
||||
if (activityClassName == null) {
|
||||
activityClassName = info.name;
|
||||
activityIsUnity = candidateIsUnity;
|
||||
continue;
|
||||
}
|
||||
|
||||
// two Unity activities is a hard conflict
|
||||
// two non-Unity activities is a conflict unless we find a Unity activity later on
|
||||
if (activityIsUnity == candidateIsUnity) {
|
||||
activityConflict = true;
|
||||
if (activityIsUnity && candidateIsUnity)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (candidateIsUnity) {
|
||||
activityClassName = info.name;
|
||||
activityIsUnity = candidateIsUnity;
|
||||
activityConflict = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (activityConflict) {
|
||||
Log.e(TAG_UNITY, "Multiple choices for activity for notifications, set activity explicitly in Notification Settings");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (activityClassName == null) {
|
||||
Log.e(TAG_UNITY, "Activity class for notifications not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
return Class.forName(activityClassName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(TAG_UNITY, "Failed to find activity class: " + e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isUnityActivity(String name) {
|
||||
return name.endsWith(".UnityPlayerActivity") || name.endsWith(".UnityPlayerGameActivity");
|
||||
}
|
||||
|
||||
protected static Notification.Builder recoverBuilder(Context context, Notification notification) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Notification.Builder builder = Notification.Builder.recoverBuilder(context, notification);
|
||||
// extras not recovered, transfer manually
|
||||
builder.setExtras(notification.extras);
|
||||
return builder;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to recover builder for notification!", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to recover builder for notification!", e);
|
||||
}
|
||||
|
||||
return recoverBuilderCustom(context, notification);
|
||||
}
|
||||
|
||||
private static Notification.Builder recoverBuilderCustom(Context context, Notification notification) {
|
||||
String channelID = notification.extras.getString(KEY_CHANNEL_ID);
|
||||
Notification.Builder builder = UnityNotificationManager.getNotificationManagerImpl(context).createNotificationBuilder(channelID);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_SMALL_ICON, notification.extras.getString(KEY_SMALL_ICON));
|
||||
String largeIcon = notification.extras.getString(KEY_LARGE_ICON);
|
||||
if (largeIcon != null && !largeIcon.isEmpty())
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_LARGE_ICON, largeIcon);
|
||||
builder.setContentTitle(notification.extras.getString(Notification.EXTRA_TITLE));
|
||||
builder.setContentText(notification.extras.getString(Notification.EXTRA_TEXT));
|
||||
builder.setAutoCancel(0 != (notification.flags & Notification.FLAG_AUTO_CANCEL));
|
||||
if (notification.number >= 0)
|
||||
builder.setNumber(notification.number);
|
||||
String bigText = notification.extras.getString(Notification.EXTRA_BIG_TEXT);
|
||||
if (bigText != null)
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(bigText));
|
||||
|
||||
builder.setWhen(notification.when);
|
||||
String group = notification.getGroup();
|
||||
if (group != null && !group.isEmpty())
|
||||
builder.setGroup(group);
|
||||
builder.setGroupSummary(0 != (notification.flags & Notification.FLAG_GROUP_SUMMARY));
|
||||
String sortKey = notification.getSortKey();
|
||||
if (sortKey != null && !sortKey.isEmpty())
|
||||
builder.setSortKey(sortKey);
|
||||
builder.setShowWhen(notification.extras.getBoolean(Notification.EXTRA_SHOW_WHEN, false));
|
||||
Integer color = UnityNotificationManager.getNotificationColor(notification);
|
||||
if (color != null)
|
||||
UnityNotificationManager.setNotificationColor(builder, color);
|
||||
UnityNotificationManager.setNotificationUsesChronometer(builder, notification.extras.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false));
|
||||
UnityNotificationManager.setNotificationGroupAlertBehavior(builder, UnityNotificationManager.getNotificationGroupAlertBehavior(notification));
|
||||
|
||||
builder.getExtras().putInt(KEY_ID, notification.extras.getInt(KEY_ID, 0));
|
||||
builder.getExtras().putLong(KEY_REPEAT_INTERVAL, notification.extras.getLong(KEY_REPEAT_INTERVAL, 0));
|
||||
builder.getExtras().putLong(KEY_FIRE_TIME, notification.extras.getLong(KEY_FIRE_TIME, 0));
|
||||
String intentData = notification.extras.getString(KEY_INTENT_DATA);
|
||||
if (intentData != null && !intentData.isEmpty())
|
||||
builder.getExtras().putString(KEY_INTENT_DATA, intentData);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user