Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "[ios][android][docs] Supports rich content (image, audio, and video) in push notifications (#4787)" #6139

Merged
merged 1 commit into from Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion CHANGELOG.md
Expand Up @@ -12,7 +12,6 @@ This is the log of notable changes to the Expo client that are developer-facing.

### 🎉 New features

- Added rich content supports for push notifications. ([#4787](https://github.com/expo/expo/pull/4787) by [@hesyifei](https://github.com/hesyifei))
- Added `MediaLibrary.saveToAssetsAsync` function that can work without `CAMERA_ROLL` permission. ([#5678](https://github.com/expo/expo/pull/5678) by [@lukmccall](https://github.com/lukmccall))
- Added support for `Speech.getAvailableVoicesAsync()` on Android. ([#5887](f0a9d8ce87451dbce8c0a309ff917c8b26472861) by [@Mitch528](https://github.com/Mitch528))

Expand Down
Expand Up @@ -10,15 +10,11 @@
import android.os.Build;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import android.util.Log;

import com.squareup.picasso.Picasso;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.HashMap;
import java.util.Random;

Expand Down Expand Up @@ -243,66 +239,14 @@ public Intent provide() {
}

// Add icon
Notification notification;
if (!manifestUrl.equals(Constants.INITIAL_URL)) {
notificationBuilder.setLargeIcon(bitmap);
}

if (body != null) {
try {
JSONObject bodyObject = new JSONObject(body);

// Download and display the custom icon.
boolean hasCustomIcon = false;
if (bodyObject.has("_icon")) {
final String iconURL = bodyObject.getString("_icon");
final Bitmap iconBitmap = loadRemoteImage(iconURL, context);
if (iconBitmap != null) {
notificationBuilder.setLargeIcon(iconBitmap);
hasCustomIcon = true;
}
}

// Download and display the rich content (the image).
// Do not display any rich content if `isMultiple`.
if (!isMultiple && bodyObject.has("_richContent")) {
final JSONObject richContent = bodyObject.getJSONObject("_richContent");
if (richContent.has("image")) {
String imageURL;
JSONObject imageOptions = null;
if (richContent.get("image") instanceof String) {
imageURL = richContent.getString("image");
} else {
imageURL = richContent.getJSONObject("image").getString("url");
imageOptions = richContent.getJSONObject("image").getJSONObject("options");
}

boolean thumbnailHidden = false;
if (imageOptions != null && imageOptions.getBoolean("thumbnailHidden")) {
thumbnailHidden = true;
}

final Bitmap imageBitmap = loadRemoteImage(imageURL, context);
if (imageBitmap != null) {
NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle()
.bigPicture(imageBitmap)
.setBigContentTitle(message);
if (!hasCustomIcon && !thumbnailHidden) {
// Make the rich content image the thumbnail too if there's no "icon" specified.
// Ref: https://developer.android.com/training/notify-user/expanded#image-style
bigPictureStyle.bigLargeIcon(null);
notificationBuilder.setLargeIcon(imageBitmap);
}
notificationBuilder.setStyle(bigPictureStyle);
}
}
}
} catch (JSONException e) {
Log.e(TAG, "Something is wrong with the user-provided data payload: " + e.toString());
}
notification = notificationBuilder.setLargeIcon(bitmap).build();
} else {
// TODO: don't actually need to load bitmap in this case
notification = notificationBuilder.build();
}

Notification notification = notificationBuilder.build();

// Display
manager.notify(experienceId, notificationId, notification);

Expand All @@ -314,19 +258,6 @@ public Intent provide() {
});
}

private Bitmap loadRemoteImage(String imageURL, Context context) {
Bitmap imageBitmap = null;
try {
imageBitmap = Picasso.with(context).load(imageURL).get();
} catch (IOException ie) {
Log.e(TAG, "The image (" + imageURL + ") in the push notification is not loaded correctly: " + ie.toString());
} catch (IllegalStateException ise) {
Log.e(TAG, "The image URL (" + imageURL + ") in the push notification is invalid: " + ise.toString());
}

return imageBitmap;
}

private void addUnreadNotificationToMetadata(String experienceId, String message, int notificationId) {
try {
JSONObject notification = new JSONObject();
Expand Down
Expand Up @@ -2,71 +2,12 @@ import { Notifications } from 'expo';

import Constants from 'expo-constants';

const demoBodies: { [type: string]: any } = {
simple: {
title: 'Welcome to Expo!',
body: 'Native Component List is registered for push notifications.',
data: { example: 'sample data' },
},
image: {
title: 'Kodiak bear',
body:
'A Kodiak bear in Kodiak National Wildlife Refuge, Alaska, United States.\n\nSource: https://commons.wikimedia.org/wiki/File:2010-kodiak-bear-1.jpg',
richContent: {
image: 'https://upload.wikimedia.org/wikipedia/commons/7/71/2010-kodiak-bear-1.jpg',
},
data: {
trinomialName: 'Ursus arctos middendorffi',
},
},
audio: {
title: 'Moonlight',
body:
'Piano Sonata No. 14 in C sharp minor "Moonlight". Recorded 1924.\n\nSource: https://www.gutenberg.org/ebooks/10178',
richContent: {
audio: 'https://www.gutenberg.org/files/10178/10178-m/10178-m-001.mp3',
},
data: {
composer: 'Ludwig van Beethoven',
},
},
gif: {
title: 'Phenakistoscope',
body:
"Eadweard Muybridge's Phenakistoscope: A Couple Waltzing.\n\nSource: https://commons.wikimedia.org/wiki/File:Phenakistoscope_3g07690d.gif",
richContent: {
video: 'https://upload.wikimedia.org/wikipedia/commons/d/d3/Phenakistoscope_3g07690d.gif',
},
},
video: {
title: 'Out There Trailer',
body:
'By the European Southern Observatory.\n\nSource: https://www.eso.org/public/videos/OutThere_trailer_en/',
richContent: {
video: 'https://cdn.eso.org/videos/medium_podcast/OutThere_trailer_en.mp4',
},
},
imageWithCustomIcon: {
title: 'Jaguar head shot',
body:
'Potrait of a jaguar at the Milwaukee County Zoological Gardens in Milwaukee, Wisconsin.\n\nSource: https://commons.wikimedia.org/wiki/File:Jaguar_head_shot-edit2.jpg and https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Jaguar_head_icon.svg/600px-Jaguar_head_icon.svg.png',
richContent: {
image: 'https://upload.wikimedia.org/wikipedia/commons/c/c9/Jaguar_head_shot-edit2.jpg',
},
icon:
'https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Jaguar_head_icon.svg/600px-Jaguar_head_icon.svg.png',
data: {
binomialName: 'Panthera onca',
},
},
};

// In this test app we contact the Expo push service directly. You *never*
// should do this in a real app. You should always store the push tokens on your
// own server or use the local notification API if you want to notify this user.
const PUSH_ENDPOINT = 'https://expo.io/--/api/v2/push/send';

export default async function registerForPushNotificationsAsync(type: string) {
export default async function registerForPushNotificationsAsync() {
// this method assumes the user has already granted permission
// to receive remote notificartions.

Expand Down Expand Up @@ -101,8 +42,10 @@ export default async function registerForPushNotificationsAsync(type: string) {
body: JSON.stringify([
{
to: token,
title: 'Welcome to Expo!',
body: 'Native Component List is registered for push notifications.',
data: { example: 'sample data' },
_category: `${Constants.manifest.id}:welcome`,
...demoBodies[type],
},
]),
});
Expand Down
38 changes: 4 additions & 34 deletions apps/native-component-list/src/screens/NotificationScreen.tsx
@@ -1,5 +1,5 @@
import React from 'react';
import { Alert, Platform, ScrollView } from 'react-native';
import { Alert, ScrollView } from 'react-native';
import { Notifications } from 'expo';
import * as Permissions from 'expo-permissions';
import HeadingText from '../components/HeadingText';
Expand Down Expand Up @@ -43,29 +43,9 @@ export default class NotificationScreen extends React.Component {

<HeadingText>Push Notifications</HeadingText>
<ListButton
onPress={() => this._sendNotificationAsync('simple')}
onPress={this._sendNotificationAsync}
title="Send me a push notification"
/>
<ListButton
onPress={() => this._sendNotificationAsync('image')}
title="Send me a push notification with an image"
/>
<ListButton
onPress={() => this._sendNotificationAsync('audio')}
title="Send me a push notification with an audio"
/>
<ListButton
onPress={() => this._sendNotificationAsync('gif')}
title="Send me a push notification with an animated image"
/>
<ListButton
onPress={() => this._sendNotificationAsync('video')}
title="Send me a push notification with a video"
/>
<ListButton
onPress={() => this._sendNotificationAsync('imageWithCustomIcon')}
title="Send me a push notification with a image and a custom icon"
/>

<HeadingText>Custom notification categories</HeadingText>
<ListButton
Expand Down Expand Up @@ -251,20 +231,10 @@ export default class NotificationScreen extends React.Component {
Alert.alert(`Cleared the badge`);
}

_sendNotificationAsync = async (type: string) => {
if (type !== 'simple' && type !== 'image' && Platform.OS !== 'ios') {
alert(
'While you will still receive the notification, you will not see any rich content since rich content other than images are only supported on iOS.'
);
}
if (type === 'imageWithCustomIcon' && Platform.OS === 'ios') {
alert(
'While you will still receive the notification, you will not see any custom icon since custom icons are not supported on iOS.'
);
}
_sendNotificationAsync = async () => {
const permission = await this._obtainRemoteNotifPermissionsAsync();
if (permission.status === 'granted') {
registerForPushNotificationsAsync(type);
registerForPushNotificationsAsync();
}
}
}
84 changes: 0 additions & 84 deletions apps/test-suite/tests/Notifications.js
Expand Up @@ -249,89 +249,5 @@ export async function test(t) {
});
});
}

// In this test app we contact the Expo push service directly. You *never*
// should do this in a real app. You should always store the push tokens on your
// own server or use the local notification API if you want to notify this user.
const PUSH_ENDPOINT = 'https://expo.io/--/api/v2/push/send';
const demoBodies = {
simple: {
title: 'Welcome to Expo!',
body: 'Native Component List is registered for push notifications.',
data: { example: 'sample data' },
},
image: {
title: 'Kodiak bear',
body:
'A Kodiak bear in Kodiak National Wildlife Refuge, Alaska, United States.\n\nSource: https://commons.wikimedia.org/wiki/File:2010-kodiak-bear-1.jpg',
richContent: {
image: 'https://upload.wikimedia.org/wikipedia/commons/7/71/2010-kodiak-bear-1.jpg',
},
data: {
trinomialName: 'Ursus arctos middendorffi',
},
},
};
t.describe('Push notifications related', () => {
async function sendPushNotificationAsync(type) {
let receivedPushNotification = false;
const subscription = Notifications.addListener(async notification => {
await testNotificationResponse(notification, demoBodies[type], type);
subscription.remove();
receivedPushNotification = true;
});
const token = await Notifications.getExpoPushTokenAsync();
const response = await fetch(PUSH_ENDPOINT, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify([
{
to: token,
...demoBodies[type],
},
]),
});

// Wait for the push notification to arrive.
await waitFor(5000);

return receivedPushNotification;
}

async function testNotificationResponse(notification, sentMessage, type) {
t.expect(notification).toBeDefined();
switch (type) {
case 'simple':
t.expect(notification.data).toEqual(sentMessage.data);
break;
case 'image': {
const newData = { ...sentMessage.data, _richContent: sentMessage.richContent };
t.expect(notification.data).toEqual(newData);
break;
}
default:
break;
}
}

t.it(
'Simple push notification',
async () => {
t.expect(await sendPushNotificationAsync('simple')).toBe(true);
},
10000
);

t.it(
'Image push notification',
async () => {
t.expect(await sendPushNotificationAsync('image')).toBe(true);
},
10000
);
});
});
}