2
0
mirror of synced 2025-01-11 14:44:12 +00:00

[notifications] Add support for android actions

This commit is contained in:
Chris Bianca 2018-03-09 11:09:20 +00:00
parent 983bbbcada
commit e537955212
12 changed files with 539 additions and 103 deletions

View File

@ -110,23 +110,28 @@ public class BundleJSONConverter {
SETTERS.put(JSONArray.class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
JSONArray jsonArray = (JSONArray) value;
ArrayList<String> stringArrayList = new ArrayList<String>();
// Empty list, can't even figure out the type, assume an ArrayList<String>
if (jsonArray.length() == 0) {
bundle.putStringArrayList(key, stringArrayList);
bundle.putStringArrayList(key, new ArrayList<String>());
return;
}
// Only strings are supported for now
for (int i = 0; i < jsonArray.length(); i++) {
Object current = jsonArray.get(i);
if (current instanceof String) {
stringArrayList.add((String) current);
} else {
throw new IllegalArgumentException("Unexpected type in an array: " + current.getClass());
if (jsonArray.get(0) instanceof String) {
ArrayList<String> stringArrayList = new ArrayList<String>();
for (int i = 0; i < jsonArray.length(); i++) {
stringArrayList.add((String) jsonArray.get(i));
}
bundle.putStringArrayList(key, stringArrayList);
} else if (jsonArray.get(0) instanceof JSONObject) {
ArrayList<Bundle> bundleArrayList = new ArrayList<>();
for (int i =0; i < jsonArray.length(); i++) {
bundleArrayList.add(convertToBundle((JSONObject) jsonArray.get(i)));
}
bundle.putSerializable(key, bundleArrayList);
} else {
throw new IllegalArgumentException("Unexpected type in an array: " + jsonArray.get(0).getClass());
}
bundle.putStringArrayList(key, stringArrayList);
}
@Override
@ -152,13 +157,18 @@ public class BundleJSONConverter {
continue;
}
// Special case List<String> as getClass would not work, since List is an interface
// Special case List<?> as getClass would not work, since List is an interface
if (value instanceof List<?>) {
JSONArray jsonArray = new JSONArray();
@SuppressWarnings("unchecked")
List<String> listValue = (List<String>) value;
for (String stringValue : listValue) {
jsonArray.put(stringValue);
List<Object> listValue = (List<Object>) value;
for (Object objValue : listValue) {
if (objValue instanceof String) {
jsonArray.put(objValue);
} else if (objValue instanceof Bundle) {
jsonArray.put(convertToJSON((Bundle) objValue));
} else {
throw new IllegalArgumentException("Unsupported type: " + objValue.getClass());
}
}
json.put(key, jsonArray);
continue;

View File

@ -17,7 +17,9 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
@ -325,10 +327,8 @@ public class RNFirebaseNotificationManager {
}
if (android.containsKey("smallIcon")) {
Bundle smallIcon = android.getBundle("smallIcon");
int smallIconResourceId = getResourceId("mipmap", smallIcon.getString("icon"));
if (smallIconResourceId == 0) {
smallIconResourceId = getResourceId("drawable", smallIcon.getString("icon"));
}
int smallIconResourceId = getIcon(smallIcon.getString("icon"));
if (smallIconResourceId != 0) {
if (smallIcon.containsKey("level")) {
Double level = smallIcon.getDouble("level");
@ -386,17 +386,17 @@ public class RNFirebaseNotificationManager {
notification.setStyle(bigPicture);
} */
// Create the notification intent
Intent intent = new Intent(context, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(notification);
if (android.containsKey("clickAction")) {
intent.setAction(android.getString("clickAction"));
// Build any actions
if (android.containsKey("actions")) {
List<Bundle> actions = (List) android.getSerializable("actions");
for (Bundle a : actions) {
NotificationCompat.Action action = createAction(a, intentClass, notification);
nb = nb.addAction(action);
}
}
PendingIntent contentIntent = PendingIntent.getActivity(context, notificationId.hashCode(), intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// Create the notification intent
PendingIntent contentIntent = createIntent(intentClass, notification, android.getString("clickAction"));
nb = nb.setContentIntent(contentIntent);
// Build the notification and send it
@ -415,6 +415,75 @@ public class RNFirebaseNotificationManager {
}
}
private NotificationCompat.Action createAction(Bundle action, Class intentClass, Bundle notification) {
String actionKey = action.getString("action");
PendingIntent actionIntent = createIntent(intentClass, notification, actionKey);
int icon = getIcon(action.getString("icon"));
String title = action.getString("title");
NotificationCompat.Action.Builder ab = new NotificationCompat.Action.Builder(icon, title, actionIntent);
if (action.containsKey("allowGeneratedReplies")) {
ab = ab.setAllowGeneratedReplies(action.getBoolean("allowGeneratedReplies"));
}
if (action.containsKey("remoteInputs")) {
List<Bundle> remoteInputs = (List) action.getSerializable("remoteInputs");
for (Bundle ri : remoteInputs) {
RemoteInput remoteInput = createRemoteInput(ri);
ab = ab.addRemoteInput(remoteInput);
}
}
// TODO: SemanticAction and ShowsUserInterface only available on v28?
// if (action.containsKey("semanticAction")) {
// Double semanticAction = action.getDouble("semanticAction");
// ab = ab.setSemanticAction(semanticAction.intValue());
// }
// if (action.containsKey("showsUserInterface")) {
// ab = ab.setShowsUserInterface(action.getBoolean("showsUserInterface"));
// }
return ab.build();
}
private PendingIntent createIntent(Class intentClass, Bundle notification, String action) {
Intent intent = new Intent(context, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(notification);
if (action != null) {
intent.setAction(action);
}
String notificationId = notification.getString("notificationId");
return PendingIntent.getActivity(context, notificationId.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
private RemoteInput createRemoteInput(Bundle remoteInput) {
String resultKey = remoteInput.getString("resultKey");
RemoteInput.Builder rb = new RemoteInput.Builder(resultKey);
if (remoteInput.containsKey("allowedDataTypes")) {
List<Bundle> allowedDataTypes = (List) remoteInput.getSerializable("allowedDataTypes");
for (Bundle adt : allowedDataTypes) {
rb.setAllowDataType(adt.getString("mimeType"), adt.getBoolean("allow"));
}
}
if (remoteInput.containsKey("allowFreeFormInput")) {
rb.setAllowFreeFormInput(remoteInput.getBoolean("allowFreeFormInput"));
}
if (remoteInput.containsKey("choices")) {
rb.setChoices(remoteInput.getStringArray("choices"));
}
if (remoteInput.containsKey("label")) {
rb.setLabel(remoteInput.getString("label"));
}
return rb.build();
}
private Bitmap getBitmap(String image) {
if (image.startsWith("http://") || image.startsWith("https://")) {
return getBitmapFromUrl(image);
@ -436,6 +505,14 @@ public class RNFirebaseNotificationManager {
}
}
private int getIcon(String icon) {
int smallIconResourceId = getResourceId("mipmap", icon);
if (smallIconResourceId == 0) {
smallIconResourceId = getResourceId("drawable", icon);
}
return smallIconResourceId;
}
private Class getMainActivityClass() {
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
@ -555,9 +632,9 @@ public class RNFirebaseNotificationManager {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationId.hashCode(),
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (schedule.containsKey("interval")) {
if (schedule.containsKey("repeatInterval")) {
Long interval = null;
switch (schedule.getString("interval")) {
switch (schedule.getString("repeatInterval")) {
case "minute":
interval = 60000L;
break;

View File

@ -7,12 +7,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
@ -198,6 +198,12 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
notificationOpenMap.putString("action", intent.getAction());
notificationOpenMap.putMap("notification", notificationMap);
// Check for remote input results
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
notificationOpenMap.putMap("results", Arguments.makeNativeMap(remoteInput));
}
return notificationOpenMap;
}

View File

@ -0,0 +1,150 @@
/**
* @flow
* AndroidAction representation wrapper
*/
import RemoteInput, {
fromNativeAndroidRemoteInput,
} from './AndroidRemoteInput';
import { SemanticAction } from './types';
import type { NativeAndroidAction, SemanticActionType } from './types';
export default class AndroidAction {
_action: string;
_allowGeneratedReplies: boolean | void;
_icon: string;
_remoteInputs: RemoteInput[];
_semanticAction: SemanticActionType | void;
_showUserInterface: boolean | void;
_title: string;
constructor(action: string, icon: string, title: string) {
this._action = action;
this._icon = icon;
this._remoteInputs = [];
this._title = title;
}
get action(): string {
return this._action;
}
get allowGeneratedReplies(): ?boolean {
return this._allowGeneratedReplies;
}
get icon(): string {
return this._icon;
}
get remoteInputs(): RemoteInput[] {
return this._remoteInputs;
}
get semanticAction(): ?SemanticActionType {
return this._semanticAction;
}
get showUserInterface(): ?boolean {
return this._showUserInterface;
}
get title(): string {
return this._title;
}
/**
*
* @param remoteInput
* @returns {AndroidAction}
*/
addRemoteInput(remoteInput: RemoteInput): AndroidAction {
if (!(remoteInput instanceof RemoteInput)) {
throw new Error(
`AndroidAction:addRemoteInput expects an 'RemoteInput' but got type ${typeof remoteInput}`
);
}
this._remoteInputs.push(remoteInput);
return this;
}
/**
*
* @param allowGeneratedReplies
* @returns {AndroidAction}
*/
setAllowGenerateReplies(allowGeneratedReplies: boolean): AndroidAction {
this._allowGeneratedReplies = allowGeneratedReplies;
return this;
}
/**
*
* @param semanticAction
* @returns {AndroidAction}
*/
setSemanticAction(semanticAction: SemanticActionType): AndroidAction {
if (!Object.values(SemanticAction).includes(semanticAction)) {
throw new Error(
`AndroidAction:setSemanticAction Invalid Semantic Action: ${semanticAction}`
);
}
this._semanticAction = semanticAction;
return this;
}
/**
*
* @param showUserInterface
* @returns {AndroidAction}
*/
setShowUserInterface(showUserInterface: boolean): AndroidAction {
this._showUserInterface = showUserInterface;
return this;
}
build(): NativeAndroidAction {
if (!this._action) {
throw new Error('AndroidAction: Missing required `action` property');
} else if (!this._icon) {
throw new Error('AndroidAction: Missing required `icon` property');
} else if (!this._title) {
throw new Error('AndroidAction: Missing required `title` property');
}
return {
action: this._action,
allowGeneratedReplies: this._allowGeneratedReplies,
icon: this._icon,
remoteInputs: this._remoteInputs.map(remoteInput => remoteInput.build()),
semanticAction: this._semanticAction,
showUserInterface: this._showUserInterface,
title: this._title,
};
}
}
export const fromNativeAndroidAction = (
nativeAction: NativeAndroidAction
): AndroidAction => {
const action = new AndroidAction(
nativeAction.action,
nativeAction.icon,
nativeAction.title
);
if (nativeAction.allowGeneratedReplies) {
action.setAllowGenerateReplies(nativeAction.allowGeneratedReplies);
}
if (nativeAction.remoteInputs) {
nativeAction.remoteInputs.forEach(remoteInput => {
action.addRemoteInput(fromNativeAndroidRemoteInput(remoteInput));
});
}
if (nativeAction.semanticAction) {
action.setSemanticAction(nativeAction.semanticAction);
}
if (nativeAction.showUserInterface) {
action.setShowUserInterface(nativeAction.showUserInterface);
}
return action;
};

View File

@ -2,6 +2,7 @@
* @flow
* AndroidChannel representation wrapper
*/
import { Importance, Visibility } from './types';
import type { ImportanceType, VisibilityType } from './types';
type NativeAndroidChannel = {|
@ -31,6 +32,15 @@ export default class AndroidChannel {
_sound: string | void;
_vibrationPattern: number[] | void;
constructor(channelId: string, name: string, importance: ImportanceType) {
if (!Object.values(Importance).includes(importance)) {
throw new Error(`AndroidChannel() Invalid Importance: ${importance}`);
}
this._channelId = channelId;
this._name = name;
this._importance = importance;
}
get bypassDnd(): ?boolean {
return this._bypassDnd;
}
@ -85,16 +95,6 @@ export default class AndroidChannel {
return this;
}
/**
*
* @param channelId
* @returns {AndroidChannel}
*/
setChannelId(channelId: string): AndroidChannel {
this._channelId = channelId;
return this;
}
/**
*
* @param description
@ -115,16 +115,6 @@ export default class AndroidChannel {
return this;
}
/**
*
* @param importance
* @returns {AndroidChannel}
*/
setImportance(importance: ImportanceType): AndroidChannel {
this._importance = importance;
return this;
}
/**
*
* @param lightColor
@ -143,20 +133,15 @@ export default class AndroidChannel {
setLockScreenVisibility(
lockScreenVisibility: VisibilityType
): AndroidChannel {
if (!Object.values(Visibility).includes(lockScreenVisibility)) {
throw new Error(
`AndroidChannel:setLockScreenVisibility Invalid Visibility: ${lockScreenVisibility}`
);
}
this._lockScreenVisibility = lockScreenVisibility;
return this;
}
/**
*
* @param name
* @returns {AndroidChannel}
*/
setName(name: string): AndroidChannel {
this._name = name;
return this;
}
/**
*
* @param showBadge

View File

@ -8,10 +8,15 @@ type NativeAndroidChannelGroup = {|
name: string,
|};
export default class AndroidChannel {
export default class AndroidChannelGroup {
_groupId: string;
_name: string;
constructor(groupId: string, name: string) {
this._groupId = groupId;
this._name = name;
}
get groupId(): string {
return this._groupId;
}
@ -20,26 +25,6 @@ export default class AndroidChannel {
return this._name;
}
/**
*
* @param groupId
* @returns {AndroidChannel}
*/
setGroupId(groupId: string): AndroidChannel {
this._groupId = groupId;
return this;
}
/**
*
* @param name
* @returns {AndroidChannel}
*/
setName(name: string): AndroidChannel {
this._name = name;
return this;
}
build(): NativeAndroidChannelGroup {
if (!this._groupId) {
throw new Error(

View File

@ -2,7 +2,8 @@
* @flow
* AndroidNotification representation wrapper
*/
import { Category } from './types';
import AndroidAction, { fromNativeAndroidAction } from './AndroidAction';
import { BadgeIconType, Category, GroupAlert, Priority } from './types';
import type Notification from './Notification';
import type {
BadgeIconTypeType,
@ -18,8 +19,7 @@ import type {
} from './types';
export default class AndroidNotification {
// TODO optional fields
// TODO actions: Action[]; // icon, title, ??pendingIntent??, allowGeneratedReplies, extender, extras, remoteinput (ugh)
_actions: AndroidAction[];
_autoCancel: boolean | void;
_badgeIconType: BadgeIconTypeType | void;
_category: CategoryType | void;
@ -70,6 +70,9 @@ export default class AndroidNotification {
this._notification = notification;
if (data) {
this._actions = data.actions
? data.actions.map(action => fromNativeAndroidAction(action))
: [];
this._autoCancel = data.autoCancel;
this._badgeIconType = data.badgeIconType;
this._category = data.category;
@ -106,12 +109,17 @@ export default class AndroidNotification {
}
// Defaults
this._actions = this._actions || [];
this._people = this._people || [];
this._smallIcon = this._smallIcon || {
icon: 'ic_launcher',
};
}
get actions(): AndroidAction[] {
return this._actions;
}
get autoCancel(): ?boolean {
return this._autoCancel;
}
@ -240,6 +248,21 @@ export default class AndroidNotification {
return this._when;
}
/**
*
* @param action
* @returns {Notification}
*/
addAction(action: AndroidAction): Notification {
if (!(action instanceof AndroidAction)) {
throw new Error(
`AndroidNotification:addAction expects an 'AndroidAction' but got type ${typeof action}`
);
}
this._actions.push(action);
return this._notification;
}
/**
*
* @param person
@ -266,6 +289,11 @@ export default class AndroidNotification {
* @returns {Notification}
*/
setBadgeIconType(badgeIconType: BadgeIconTypeType): Notification {
if (!Object.values(BadgeIconType).includes(badgeIconType)) {
throw new Error(
`AndroidNotification:setBadgeIconType Invalid BadgeIconType: ${badgeIconType}`
);
}
this._badgeIconType = badgeIconType;
return this._notification;
}
@ -277,7 +305,9 @@ export default class AndroidNotification {
*/
setCategory(category: CategoryType): Notification {
if (!Object.values(Category).includes(category)) {
throw new Error(`AndroidNotification: Invalid Category: ${category}`);
throw new Error(
`AndroidNotification:setCategory Invalid Category: ${category}`
);
}
this._category = category;
return this._notification;
@ -359,6 +389,11 @@ export default class AndroidNotification {
* @returns {Notification}
*/
setGroupAlertBehaviour(groupAlertBehaviour: GroupAlertType): Notification {
if (!Object.values(GroupAlert).includes(groupAlertBehaviour)) {
throw new Error(
`AndroidNotification:setGroupAlertBehaviour Invalid GroupAlert: ${groupAlertBehaviour}`
);
}
this._groupAlertBehaviour = groupAlertBehaviour;
return this._notification;
}
@ -445,6 +480,11 @@ export default class AndroidNotification {
* @returns {Notification}
*/
setPriority(priority: PriorityType): Notification {
if (!Object.values(Priority).includes(priority)) {
throw new Error(
`AndroidNotification:setPriority Invalid Priority: ${priority}`
);
}
this._priority = priority;
return this._notification;
}
@ -596,7 +636,7 @@ export default class AndroidNotification {
}
return {
// TODO actions: Action[],
actions: this._actions.map(action => action.build()),
autoCancel: this._autoCancel,
badgeIconType: this._badgeIconType,
category: this._category,

View File

@ -0,0 +1,123 @@
/**
* @flow
* AndroidRemoteInput representation wrapper
*/
import type { AndroidAllowDataType, NativeAndroidRemoteInput } from './types';
export default class AndroidRemoteInput {
_allowedDataTypes: AndroidAllowDataType[];
_allowFreeFormInput: boolean | void;
_choices: string[];
_label: string | void;
_resultKey: string;
constructor(resultKey: string) {
this._allowedDataTypes = [];
this._choices = [];
this._resultKey = resultKey;
}
get allowedDataTypes(): AndroidAllowDataType[] {
return this._allowedDataTypes;
}
get allowFreeFormInput(): ?boolean {
return this._allowFreeFormInput;
}
get choices(): string[] {
return this._choices;
}
get label(): ?string {
return this._label;
}
get resultKey(): string {
return this._resultKey;
}
/**
*
* @param mimeType
* @param allow
* @returns {AndroidRemoteInput}
*/
setAllowDataType(mimeType: string, allow: boolean): AndroidRemoteInput {
this._allowedDataTypes.push({
allow,
mimeType,
});
return this;
}
/**
*
* @param allowFreeFormInput
* @returns {AndroidRemoteInput}
*/
setAllowFreeFormInput(allowFreeFormInput: boolean): AndroidRemoteInput {
this._allowFreeFormInput = allowFreeFormInput;
return this;
}
/**
*
* @param choices
* @returns {AndroidRemoteInput}
*/
setChoices(choices: string[]): AndroidRemoteInput {
this._choices = choices;
return this;
}
/**
*
* @param label
* @returns {AndroidRemoteInput}
*/
setLabel(label: string): AndroidRemoteInput {
this._label = label;
return this;
}
build(): NativeAndroidRemoteInput {
if (!this._resultKey) {
throw new Error(
'AndroidRemoteInput: Missing required `resultKey` property'
);
}
return {
allowedDataTypes: this._allowedDataTypes,
allowFreeFormInput: this._allowFreeFormInput,
choices: this._choices,
label: this._label,
resultKey: this._resultKey,
};
}
}
export const fromNativeAndroidRemoteInput = (
nativeRemoteInput: NativeAndroidRemoteInput
): AndroidRemoteInput => {
const remoteInput = new AndroidRemoteInput(nativeRemoteInput.resultKey);
if (nativeRemoteInput.allowDataType) {
for (let i = 0; i < nativeRemoteInput.allowDataType.length; i++) {
const allowDataType = nativeRemoteInput.allowDataType[i];
remoteInput.setAllowDataType(allowDataType.mimeType, allowDataType.allow);
}
}
if (nativeRemoteInput.allowFreeFormInput) {
remoteInput.setAllowFreeFormInput(nativeRemoteInput.allowFreeFormInput);
}
if (nativeRemoteInput.choices) {
remoteInput.setChoices(nativeRemoteInput.choices);
}
if (nativeRemoteInput.label) {
remoteInput.setLabel(nativeRemoteInput.label);
}
return remoteInput;
};

View File

@ -9,10 +9,11 @@ import { generatePushID, isObject } from '../../utils';
import type { NativeNotification } from './types';
export type NotificationOpen = {
export type NotificationOpen = {|
action: string,
notification: Notification,
};
results?: { [string]: string },
|};
export default class Notification {
// iOS 8/9 | 10+ | Android

View File

@ -7,9 +7,11 @@ import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils';
import AndroidAction from './AndroidAction';
import AndroidChannel from './AndroidChannel';
import AndroidChannelGroup from './AndroidChannelGroup';
import AndroidNotifications from './AndroidNotifications';
import AndroidRemoteInput from './AndroidRemoteInput';
import Notification from './Notification';
import {
BadgeIconType,
@ -18,6 +20,7 @@ import {
GroupAlert,
Importance,
Priority,
SemanticAction,
Visibility,
} from './types';
@ -99,6 +102,7 @@ export default class Notifications extends ModuleBase {
SharedEventEmitter.emit('onNotificationOpened', {
action: notificationOpen.action,
notification: new Notification(notificationOpen.notification),
results: notificationOpen.results,
});
}
);
@ -166,6 +170,7 @@ export default class Notifications extends ModuleBase {
return {
action: notificationOpen.action,
notification: new Notification(notificationOpen.notification),
results: notificationOpen.results,
};
}
return null;
@ -295,6 +300,7 @@ export default class Notifications extends ModuleBase {
export const statics = {
Android: {
Action: AndroidAction,
BadgeIconType,
Category,
Channel: AndroidChannel,
@ -303,6 +309,8 @@ export const statics = {
GroupAlert,
Importance,
Priority,
RemoteInput: AndroidRemoteInput,
SemanticAction,
Visibility,
},
Notification,

View File

@ -1,7 +1,6 @@
/**
* @flow
*/
export const BadgeIconType = {
Large: 2,
None: 0,
@ -57,6 +56,20 @@ export const Priority = {
Min: -2,
};
export const SemanticAction = {
Archive: 5,
Call: 10,
Delete: 4,
MarkAsRead: 2,
MarkAsUnread: 3,
Mute: 6,
None: 0,
Reply: 1,
ThumbsDown: 9,
ThumbsUp: 8,
Unmute: 7,
};
export const Visibility = {
Private: 0,
Public: 1,
@ -69,27 +82,51 @@ export type DefaultsType = $Values<typeof Defaults>;
export type GroupAlertType = $Values<typeof GroupAlert>;
export type ImportanceType = $Values<typeof Importance>;
export type PriorityType = $Values<typeof Priority>;
export type SemanticActionType = $Values<typeof SemanticAction>;
export type VisibilityType = $Values<typeof Visibility>;
export type Lights = {
export type Lights = {|
argb: number,
onMs: number,
offMs: number,
};
|};
export type Progress = {
export type Progress = {|
max: number,
progress: number,
indeterminate: boolean,
};
|};
export type SmallIcon = {
export type SmallIcon = {|
icon: string,
level?: number,
|};
export type AndroidAllowDataType = {
allow: boolean,
mimeType: string,
};
export type NativeAndroidRemoteInput = {|
allowedDataTypes: AndroidAllowDataType[],
allowFreeFormInput?: boolean,
choices: string[],
label?: string,
resultKey: string,
|};
export type NativeAndroidAction = {|
action: string,
allowGeneratedReplies?: boolean,
icon: string,
remoteInputs: NativeAndroidRemoteInput[],
semanticAction?: SemanticActionType,
showUserInterface?: boolean,
title: string,
|};
export type NativeAndroidNotification = {|
// TODO actions: Action[],
actions?: NativeAndroidAction[],
autoCancel?: boolean,
badgeIconType?: BadgeIconTypeType,
category?: CategoryType,
@ -154,11 +191,11 @@ export type NativeIOSNotification = {|
threadIdentifier?: string,
|};
export type Schedule = {
export type Schedule = {|
exact?: boolean,
fireDate: number,
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
};
|};
export type NativeNotification = {|
android?: NativeAndroidNotification,
@ -175,4 +212,5 @@ export type NativeNotification = {|
export type NativeNotificationOpen = {|
action: string,
notification: NativeNotification,
results?: { [string]: string },
|};

View File

@ -37,22 +37,35 @@ const init = async () => {
console.log('onNotificationDisplayed: ', notification);
});
// RNfirebase.instanceid().delete();
const channel = new RNfirebase.notifications.Android.Channel();
channel
.setChannelId('test')
.setName('test')
.setImportance(RNfirebase.notifications.Android.Importance.Max)
.setDescription('test channel');
const channel = new RNfirebase.notifications.Android.Channel(
'test',
'test',
RNfirebase.notifications.Android.Importance.Max
);
channel.setDescription('test channel');
RNfirebase.notifications().android.createChannel(channel);
const remoteInput = new RNfirebase.notifications.Android.RemoteInput(
'inputText'
);
remoteInput.setLabel('Message');
const action = new RNfirebase.notifications.Android.Action(
'test_action',
'ic_launcher',
'My Test Action'
);
action.addRemoteInput(remoteInput);
const notification = new RNfirebase.notifications.Notification();
notification
.setTitle('Test title')
.setBody('Test body')
.setNotificationId('displayed')
.android.addAction(action)
.android.setChannelId('test')
.android.setClickAction('action')
.android.setPriority(RNfirebase.notifications.Android.Priority.Max);
const date = new Date();
date.setMinutes(date.getMinutes() + 1);
setTimeout(() => {