supporting unified push as default if detected

This commit is contained in:
Roland Osborne 2023-06-09 20:27:04 -07:00
parent 42fedce27a
commit 855096d0ee
9 changed files with 194 additions and 16 deletions

View File

@ -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);
},

View File

@ -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'

View File

@ -16,6 +16,7 @@
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".MainApplication"
@ -73,5 +74,19 @@
</intent-filter>
</activity>
<receiver
android:enabled="true"
android:name=".CustomReceiver"
android:exported="true">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT"/>
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED"/>
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED"/>
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -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());
}
}

View File

@ -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<String>(),
getApplicationContext().getPackageName()
);
}
});
}
/**

View File

@ -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'
}
}
}
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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) {