diff --git a/app/mobile/App.js b/app/mobile/App.js
index caabfdaa..7fdd944e 100644
--- a/app/mobile/App.js
+++ b/app/mobile/App.js
@@ -18,6 +18,7 @@ import { Access } from 'src/access/Access';
import { Dashboard } from 'src/dashboard/Dashboard';
import { Session } from 'src/session/Session';
import ReceiveSharingIntent from 'react-native-receive-sharing-intent';
+import {PermissionsAndroid} from 'react-native';
// silence warning: Sending `onAnimatedValueUpdate` with no listeners registered
//LogBox.ignoreLogs(['Sending']);
@@ -27,6 +28,9 @@ export default function App() {
const [sharing, setSharing] = useState();
useEffect(() => {
+
+ PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
+
ReceiveSharingIntent.getReceivedFiles(files => {
setSharing(files);
},
diff --git a/app/mobile/android/app/build.gradle b/app/mobile/android/app/build.gradle
index 9c0d284b..759e5400 100644
--- a/app/mobile/android/app/build.gradle
+++ b/app/mobile/android/app/build.gradle
@@ -158,8 +158,12 @@ dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
+ implementation 'androidx.core:core:1.8.0'
+
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
+ implementation 'com.github.UnifiedPush:android-connector:2.1.1'
+
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.squareup.okhttp3', module:'okhttp'
diff --git a/app/mobile/android/app/src/main/AndroidManifest.xml b/app/mobile/android/app/src/main/AndroidManifest.xml
index 77235871..a02cfc4c 100644
--- a/app/mobile/android/app/src/main/AndroidManifest.xml
+++ b/app/mobile/android/app/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/mobile/android/app/src/main/java/com/databag/CustomReceiver.java b/app/mobile/android/app/src/main/java/com/databag/CustomReceiver.java
new file mode 100644
index 00000000..7152ebe4
--- /dev/null
+++ b/app/mobile/android/app/src/main/java/com/databag/CustomReceiver.java
@@ -0,0 +1,103 @@
+package com.databag;
+
+import android.content.Context;
+import org.jetbrains.annotations.NotNull;
+import org.unifiedpush.android.connector.MessagingReceiver;
+import android.util.Log;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManager;
+import android.os.Build;
+import android.net.Uri;
+import android.media.RingtoneManager;
+import androidx.core.app.NotificationCompat;
+import java.nio.charset.StandardCharsets;
+
+import com.facebook.react.ReactApplication;
+import com.facebook.react.ReactInstanceManager;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.modules.core.DeviceEventManagerModule;
+
+public class CustomReceiver extends MessagingReceiver {
+ public CustomReceiver() {
+ super();
+ }
+
+ private boolean forgrounded () {
+ ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
+ ActivityManager.getMyMemoryState(appProcessInfo);
+ return (appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND || appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_VISIBLE);
+ }
+
+ @Override
+ public void onNewEndpoint(@NotNull Context context, @NotNull String endpoint, @NotNull String instance) {
+
+ final ReactInstanceManager reactInstanceManager =
+ ((ReactApplication) context.getApplicationContext())
+ .getReactNativeHost()
+ .getReactInstanceManager();
+ ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
+
+ WritableMap params = Arguments.createMap();
+ params.putString("instance", instance);
+ params.putString("endpoint", endpoint);
+ reactContext
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
+ .emit("unifiedPushURL", params);
+
+ // Called when a new endpoint be used for sending push messages
+ }
+
+ @Override
+ public void onRegistrationFailed(@NotNull Context context, @NotNull String instance) {
+ // called when the registration is not possible, eg. no network
+ }
+
+ @Override
+ public void onUnregistered(@NotNull Context context, @NotNull String instance) {
+ // called when this application is unregistered from receiving push messages
+ }
+
+ @Override
+ public void onMessage(@NotNull Context context, @NotNull byte[] message, @NotNull String instance) {
+
+ if (forgrounded()) {
+ return;
+ }
+
+ String strMessage = new String(message, StandardCharsets.UTF_8);
+
+
+ Intent intent = new Intent(context, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 /* Request code */, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ String channelId = "fcm_default_channel";
+ Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+ NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context,
+ channelId)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentTitle(strMessage).setAutoCancel(true).setSound(
+ defaultSoundUri).setContentIntent(pendingIntent);
+
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(channelId, "Channel human readable title",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ notificationManager.createNotificationChannel(channel);
+ }
+
+ notificationManager.notify(0, notificationBuilder.build());
+ }
+}
+
diff --git a/app/mobile/android/app/src/main/java/com/databag/MainActivity.java b/app/mobile/android/app/src/main/java/com/databag/MainActivity.java
index e653c22c..a7170a13 100644
--- a/app/mobile/android/app/src/main/java/com/databag/MainActivity.java
+++ b/app/mobile/android/app/src/main/java/com/databag/MainActivity.java
@@ -1,5 +1,6 @@
package com.databag;
+import java.util.ArrayList;
import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.ReactActivity;
@@ -7,6 +8,19 @@ import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
+import org.unifiedpush.android.connector.UnifiedPush;
+
+import android.content.Context;
+
+import org.unifiedpush.android.connector.RegistrationDialogContent;
+
+import com.facebook.react.ReactApplication;
+import com.facebook.react.ReactInstanceManager;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.modules.core.DeviceEventManagerModule;
+
public class MainActivity extends ReactActivity {
/**
@@ -21,6 +35,25 @@ public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
+ MainActivity activityContext = this;
+
+ this.getSharedPreferences("unifiedpush.connector", Context.MODE_PRIVATE).edit().putBoolean("unifiedpush.no_distrib_dialog", true).apply();
+
+
+ ReactInstanceManager mReactInstanceManager = getReactNativeHost().getReactInstanceManager();
+ mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
+ public void onReactContextInitialized(ReactContext validContext) {
+
+ UnifiedPush.registerAppWithDialog(
+ activityContext,
+ "default",
+ new RegistrationDialogContent(),
+ new ArrayList(),
+ getApplicationContext().getPackageName()
+ );
+
+ }
+ });
}
/**
diff --git a/app/mobile/android/build.gradle b/app/mobile/android/build.gradle
index 44b54cc7..83743b74 100644
--- a/app/mobile/android/build.gradle
+++ b/app/mobile/android/build.gradle
@@ -21,3 +21,14 @@ buildscript {
classpath("com.facebook.react:react-native-gradle-plugin")
}
}
+
+allprojects {
+ repositories {
+ maven {
+ url "https://www.jitpack.io"
+ content {
+ includeModule 'com.github.UnifiedPush', 'android-connector'
+ }
+ }
+ }
+}
diff --git a/app/mobile/src/api/setAccountAccess.js b/app/mobile/src/api/setAccountAccess.js
index d7949e7d..233968a3 100644
--- a/app/mobile/src/api/setAccountAccess.js
+++ b/app/mobile/src/api/setAccountAccess.js
@@ -1,7 +1,7 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
-export async function setAccountAccess(server, token, appName, appVersion, platform, deviceToken, notifications) {
- let access = await fetchWithTimeout(`https://${server}/account/access?token=${token}&appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}`, { method: 'PUT', body: JSON.stringify(notifications) })
+export async function setAccountAccess(server, token, appName, appVersion, platform, deviceToken, pushType, notifications) {
+ let access = await fetchWithTimeout(`https://${server}/account/access?token=${token}&appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}&pushType=${pushType}`, { method: 'PUT', body: JSON.stringify(notifications) })
checkResponse(access)
return await access.json()
}
diff --git a/app/mobile/src/api/setLogin.js b/app/mobile/src/api/setLogin.js
index 3487da97..0d1c49c7 100644
--- a/app/mobile/src/api/setLogin.js
+++ b/app/mobile/src/api/setLogin.js
@@ -1,10 +1,10 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
import base64 from 'react-native-base64'
-export async function setLogin(username, server, password, appName, appVersion, platform, deviceToken, notifications) {
+export async function setLogin(username, server, password, appName, appVersion, platform, deviceToken, pushType, notifications) {
let headers = new Headers()
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));
- let login = await fetchWithTimeout(`https://${server}/account/apps?appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}`, { method: 'POST', body: JSON.stringify(notifications), headers: headers })
+ let login = await fetchWithTimeout(`https://${server}/account/apps?appName=${appName}&appVersion=${appVersion}&platform=${platform}&deviceToken=${deviceToken}&pushType=${pushType}`, { method: 'POST', body: JSON.stringify(notifications), headers: headers })
checkResponse(login)
return await login.json()
}
diff --git a/app/mobile/src/context/useAppContext.hook.js b/app/mobile/src/context/useAppContext.hook.js
index 2abddb38..bf7025fa 100644
--- a/app/mobile/src/context/useAppContext.hook.js
+++ b/app/mobile/src/context/useAppContext.hook.js
@@ -14,6 +14,7 @@ import { ChannelContext } from 'context/ChannelContext';
import { RingContext } from 'context/RingContext';
import { getVersion, getApplicationName, getDeviceId } from 'react-native-device-info'
import messaging from '@react-native-firebase/messaging';
+import { DeviceEventEmitter } from 'react-native';
export function useAppContext() {
const [state, setState] = useState({
@@ -33,6 +34,7 @@ export function useAppContext() {
const ws = useRef(null);
const deviceToken = useRef(null);
+ const pushType = useRef(null);
const access = useRef(null);
const init = useRef(false);
@@ -41,19 +43,23 @@ export function useAppContext() {
}
useEffect(() => {
+
+ // select the unified token if available
+ DeviceEventEmitter.addListener('unifiedPushURL', (e) => {
+ deviceToken.current = e.endpoint;
+ pushType.current = "up";
+ });
+
(async () => {
try {
- try {
- deviceToken.current = await messaging().getToken();
- }
- catch (err) {
- console.log(err);
- //Alert.alert('FCM', err.toString());
+ const token = await messaging().getToken();
+ if (!deviceToken.current) {
+ deviceToken.current = token;
+ pushType.current = "fcm";
}
}
catch (err) {
console.log(err);
- deviceToken.current = null;
}
access.current = await store.actions.init();
if (access.current) {
@@ -104,7 +110,7 @@ export function useAppContext() {
throw new Error('invalid session state');
}
await addAccount(server, username, password, token);
- const session = await setLogin(username, server, password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, notifications)
+ const session = await setLogin(username, server, password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, pushType.current, notifications)
access.current = { server, token: session.appToken, guid: session.guid };
await store.actions.setSession(access.current);
await setSession();
@@ -116,7 +122,7 @@ export function useAppContext() {
if (!init.current || access.current) {
throw new Error('invalid session state');
}
- const session = await setAccountAccess(server, token, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, notifications);
+ const session = await setAccountAccess(server, token, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, pushType.current, notifications);
access.current = { server, token: session.appToken, guid: session.guid };
await store.actions.setSession(access.current);
await setSession();
@@ -129,7 +135,7 @@ export function useAppContext() {
throw new Error('invalid session state');
}
const acc = username.split('@');
- const session = await setLogin(acc[0], acc[1], password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, notifications)
+ const session = await setLogin(acc[0], acc[1], password, getApplicationName(), getVersion(), getDeviceId(), deviceToken.current, pushType.current, notifications)
access.current = { server: acc[1], token: session.appToken, guid: session.guid };
await store.actions.setSession(access.current);
await setSession();
@@ -143,8 +149,10 @@ export function useAppContext() {
}
updateState({ loggingOut: true });
try {
- await messaging().deleteToken();
- deviceToken.current = await messaging().getToken();
+ if (pushType.current == "fcm") {
+ await messaging().deleteToken();
+ deviceToken.current = await messaging().getToken();
+ }
await clearLogin(state.server, state.token);
}
catch (err) {