Skip to content

Commit

Permalink
Implementation of android GCM push notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
kunall17 authored and borisyankov committed Jun 16, 2017
1 parent 8eac941 commit b37352a
Show file tree
Hide file tree
Showing 30 changed files with 446 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"dan", "abramov", "lang", "Crashlytics", "bool", "ionicons", "truthy", "bezier", "decrement", "js",
"jsonp", "otp", "Otp", "ascii", "Ascii", "substr", "Util", "actionsheet", "unmute",
"otp", "Otp", "ascii", "Ascii", "substr", "Util", "actionsheet", "jsonp", "actionsheet", "ionicons",
"denmark", "copenhagen"
"denmark", "copenhagen", "unregister", "gcm"
],
"skipIfMatch": [
"http://[^s]*",
Expand Down
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
.*/Libraries/react-native/ReactNative.js
.*/node_modules/react-static-container/.*
.*/node_modules/eslint-plugin-jsx-a11y/.*
.*/node_modules/react-native-notifications/.*

[include]

Expand Down
1 change: 1 addition & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ dependencies {
transitive = true;
}
compile 'com.android.support:customtabs:23.0.1'
compile project(':reactnativenotifications')
}

// Run this once to be able to run the application with BUCK
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
android:name="io.fabric.ApiKey"
android:value="[YOUR API KEY]"
/>
<meta-data android:name="com.wix.reactnativenotifications.gcmSenderId" android:value="835904834568\0"/>
</application>

</manifest>
67 changes: 41 additions & 26 deletions android/app/src/main/java/com/zulipmobile/MainApplication.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.zulipmobile;

import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import com.facebook.react.ReactApplication;
Expand All @@ -13,41 +15,54 @@
import com.smixx.fabric.FabricPackage;
import com.crashlytics.android.Crashlytics;
import io.fabric.sdk.android.Fabric;
import com.wix.reactnativenotifications.RNNotificationsPackage;
import com.wix.reactnativenotifications.core.AppLaunchHelper;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.notification.INotificationsApplication;
import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.zulipmobile.notifications.GCMPushNotifications;

import java.util.Arrays;
import java.util.List;

import com.learnium.RNDeviceInfo.RNDeviceInfo;

public class MainApplication extends Application implements ReactApplication {
public class MainApplication extends Application implements ReactApplication, INotificationsApplication {

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new FabricPackage(),
new MainReactPackage(),
new RNSoundPackage(),
new RNDeviceInfo(),
new ZulipNativePackage(),
new RNNotificationsPackage(MainApplication.this)
);
}
};

@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
//Fabric.with(this, new Crashlytics());
}

@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new FabricPackage(),
new MainReactPackage(),
new RNSoundPackage(),
new RNDeviceInfo(),
new ZulipNativePackage()
);
public IPushNotification getPushNotification(Context context, Bundle bundle, AppLifecycleFacade defaultFacade, AppLaunchHelper defaultAppLaunchHelper) {
return new GCMPushNotifications(context, bundle, defaultFacade, defaultAppLaunchHelper, new JsIOHelper());
}
};

@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}

@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
//Fabric.with(this, new Crashlytics());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.zulipmobile.notifications;

import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;

import com.wix.reactnativenotifications.core.AppLaunchHelper;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.notification.PushNotification;
import com.zulipmobile.R;

import java.io.IOException;
import java.net.URL;

public class GCMPushNotifications extends PushNotification {

public GCMPushNotifications(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
}

@Override
protected PushNotificationsProp createProps(Bundle bundle) {
return new PushNotificationsProp(bundle);
}

protected PushNotificationsProp getProps() {
return (PushNotificationsProp) mNotificationProps;
}

@Override
protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
// First, get a builder initialized with defaults from the core class.
final Notification.Builder builder = super.getNotificationBuilder(intent);

String type = getProps().getRecipientType();
String content = getProps().getContent();
String title = getProps().getSenderFullName();
String avatarURL = getProps().getAvatarURL();
String time = getProps().getTime();
String stream = getProps().getStream();
String topic = getProps().getTopic();
String baseURL = getProps().getBaseURL();

builder.setSmallIcon(R.drawable.zulip_notification);
builder.setContentTitle(title);
builder.setAutoCancel(true);
builder.setContentText(content);

if (type.equals("private")) {
if (android.os.Build.VERSION.SDK_INT >= 16) {
builder.setSubText("New private message");
}
} else if (type.equals("stream")) {
if (android.os.Build.VERSION.SDK_INT >= 16) {
String displayTopic = stream + " > "
+ topic;
builder.setSubText("Mention on " + displayTopic);
}
}
if (avatarURL != null) {
Bitmap avatar = fetchAvatar(NotificationHelper.sizedURL(mContext,
avatarURL, 64, baseURL));
if (avatar != null) {
builder.setLargeIcon(avatar);
}
}
if (time != null) {
long timStamp = Long.parseLong(getProps().getTime()) * 1000;
builder.setWhen(timStamp);
}

long[] vPattern = {0, 100, 200, 100};
builder.setVibrate(vPattern);
return builder;
}

private Bitmap fetchAvatar(URL url) {
try {
return NotificationHelper.fetch(url);
} catch (IOException e) {
Log.e("ERROR", e.toString());
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.zulipmobile.notifications;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.util.TypedValue;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class NotificationHelper {

public static Bitmap fetch(URL url) throws IOException {
Log.i("GAFT.fetch", "Getting gravatar from url: " + url);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof InputStream) {
return BitmapFactory.decodeStream((InputStream) response);
}
return null;
}

public static URL sizedURL(Context context, String url, float dpSize, String baseUrl) {
// From http://stackoverflow.com/questions/4605527/
Resources r = context.getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpSize, r.getDisplayMetrics());
try {
return new URL(addHost(url, baseUrl) + "&s=" + px);
} catch (MalformedURLException e) {
Log.e("ERROR", e.toString());
return null;
}
}

public static String addHost(String url, String baseURL) {
if (!url.startsWith("http")) {
if (baseURL.endsWith("/")) {
url = baseURL.substring(0, baseURL.length() - 1) + url;
} else {
url = baseURL + url;
}
}
return url;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.zulipmobile.notifications;

import android.os.Bundle;

import com.wix.reactnativenotifications.core.notification.PushNotificationProps;

public class PushNotificationsProp extends PushNotificationProps {

public PushNotificationsProp(Bundle bundle) {
super(bundle);
}

public String getRecipientType() {
return mBundle.getString("recipient_type");
}

public String getContent() {
return mBundle.getString("content");
}

public String getSenderFullName() {
return mBundle.getString("sender_full_name");
}

public String getAvatarURL() {
return mBundle.getString("sender_avatar_url");

}

public String getStream() {
return mBundle.getString("stream");
}

public String getTopic() {
return mBundle.getString("topic");
}

public String getTime() {
return mBundle.getString("time");
}

@Override
protected PushNotificationsProp copy() {
return new PushNotificationsProp((Bundle) mBundle.clone());
}

public String getEmail() {
return mBundle.getString("sender_email");
}

public String getBaseURL() {
return mBundle.getString("base_url");
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ include ':react-native-fabric'
project(':react-native-fabric').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fabric/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')

include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android')
1 change: 1 addition & 0 deletions index.ios.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* @flow */
import { AppRegistry } from 'react-native';
import ZulipMobile from './src/ZulipMobile';

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"react-native-device-info": "^0.10.1",
"react-native-drawer": "^2.3.0",
"react-native-fabric": "^0.4.1",
"react-native-notifications": "^1.1.10",
"react-native-safari-view": "^2.0.0",
"react-native-scrollable-tab-view": "^0.7.0",
"react-native-sound": "^0.10.1",
Expand Down
24 changes: 18 additions & 6 deletions src/account-info/LogoutButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { connect } from 'react-redux';

import { InitRouteAction } from '../types';
import { InitRouteAction, Auth } from '../types';
import boundActions from '../boundActions';
import { getInitialRoutes } from '../nav/routingSelectors';
import { ZulipButton } from '../common';
import unregisterGCM from '../api/unregisterGCM';
import { getAuth } from '../account/accountSelectors';

const styles = StyleSheet.create({
logoutButton: {
Expand All @@ -20,14 +22,22 @@ class LogoutButton extends Component {
props: {
accounts: any[],
initRoutes: InitRouteAction,
logout: (accounts: any[]) => void
logout: (accounts: any[]) => void,
deleteTokenGCM: () => void,
auth: Auth,
gcmToken: string
};

logout = () => {
this.props.logout(this.props.accounts);
const accountsLoggedOut = this.props.accounts.slice();
logout = async () => {
const { accounts, auth, deleteTokenGCM, gcmToken } = this.props;
if (gcmToken !== '') {
await unregisterGCM(auth, gcmToken);
deleteTokenGCM();
}
this.props.logout(accounts);
const accountsLoggedOut = accounts.slice();
accountsLoggedOut[0].apiKey = '';
this.props.initRoutes(getInitialRoutes(this.props.accounts));
this.props.initRoutes(getInitialRoutes(accounts));
}

render() {
Expand All @@ -44,7 +54,9 @@ class LogoutButton extends Component {

export default connect(
(state) => ({
auth: getAuth(state),
accounts: state.accounts,
gcmToken: state.realm.gcmToken
}),
boundActions,
)(LogoutButton);

0 comments on commit b37352a

Please sign in to comment.