Expo: How to detect a user closed browser event on Android - android

I have a credit card addition system with 3DS security enabled.
To achieve that, I have to open a browser to the bank 3DS system.
As the user has the possibility to close the opened browser tab, I have to be able to detect this event and reset the loading state.
I did it that way:
client.service('credit-cards')
.patch(creditCardRef.current._id, {
registrationId: creditCardRef.current.registrationId,
registrationData: registrationDataRef.current,
registrationIpAddress: ipAddressRef.current,
// #ts-expect-error Unable to set the type of the parsed query.
registrationBrowserInfo: {
...parsedUrl.query,
acceptHeader: headersRef.current.accept,
},
secureModeRedirectUrl: Linking.createURL('paybox-3ds', {
queryParams: { creditCardId: creditCardRef.current._id },
}),
})
.then(ResultHelpers.toOne)
.then((result) => WebBrowser.openBrowserAsync(result.secureModeUrl, {
// #see https://github.com/expo/expo/issues/8072#issuecomment-621173298
showInRecents: true,
}))
.then((browserResult: WebBrowser.WebBrowserResult): void => {
// On IOS check if the user manually closed the browser using "Done" browser button
if (browserResult.type === 'cancel') {
setLoading(false);
}
})
.catch((error) => {
handleCreditCardRegistrationError(error);
});
This is working, but only for iOS. Indeed, the official documentation says:
The promise behaves differently based on the platform.
On Android promise resolves with {type: 'opened'} if we were able to
open browser.
On iOS: If the user closed the web browser, the Promise resolves with
{ type: 'cancel' }.
What is my alternative for Android?

Related

React native: requestTechnology called only if the app is brought from background to foreground

I am currently working on a simple app for scanning, reading data from an NFC card (in my case a Mifare NFC card) and displaying in on the screen. I have built it using this example.
The app should work both on Android and iOS, but for the moment, I have tested it only on an android device (an Oppo device with NFC capablities and developer mode activated).
At the launch of the app, everything seems to be working fine, the NfcManager has been successfully started, but there is an issue when the app tries to request the technology for reading the card, namely, I have to bring the app first in the background and then again in the foreground so that the message requestTechnology success is displayed, otherwise, it's simply not called.
After this, the promise NfcManager.getTag() gets rejected with the error message: no reference available.
Here is my code:
componentDidMount() {
NfcManager.start({
onSessionClosedIOS: () => {
alert('ios session closed');
},
}).then(() => console.warn('NfcManager started')) // successfully started
.catch((error) => alert('Error starting NfcManager: ', error));
}
{... componentWillUnmount and render method ...}
_read = async () => {
try {
let tech = Platform.OS === 'ios'
? NfcTech.MifareIOS : [NfcTech.MifareClassic, NfcTech.NfcA, NfcTech.IsoDep, NfcTech.Ndef];
let resp = await NfcManager.requestTechnology(tech, {
alertMessage: 'Ready to do some custom Mifare cmd!'
})
.then((value) => alert('requestTechnology success', value)) // here the value is empty, so no NfcTech
.catch(() => console.warn('reuqestTechnology error'));
const tag = await NfcManager.getTag()
.then((value) => alert('Tag event: ', value))
.catch((err) => console.warn('error getting tag: ', err));
// this part of the code is reached, but not working properly since the tag.id is not correctly retrieved
if (Platform.OS === 'ios') {
resp = await NfcManager.sendMifareCommandIOS([0x30, 0x00]);
} else {
resp = await NfcManager.transceive([0x30, 0x00]);
}
console.warn('Response: ', resp);
this._cleanUp();
} catch (ex) {
console.warn(ex);
this._cleanUp();
}
}
If I scan the card against the device, it makes the sound like it has been scanned, but nothing seems to be displayed.
Does anyone know why does the app needs to be brought to the background so that the technology is requested? And second, does the fail of the getTag() method have anything to do with it?
I hope anyone can help me with this issue, I have been struggling with this problem for quite some time and I haven't found any solution.
May be related to github.com/revtel/react-native-nfc-manager/issues/423 ?? there seems to be a scenario where this package does not correctly configure enableForegroundDispatch and manually pausing and resuming the App by sending it to the background would fix it.

React-Native: How to access react-native app's permissions in Android?

I'm trying to access my react-native app's specific permissions in Android. I can access and pull up my app's info page in Android, but I can't go directly into the permissions screen from my app.
I am using react-native-android-open-settings to to achieve this. By using this I am able to access the app's info page, but can't access the specific permissions page for the app without the user having to click on permissions in App Info. The library can be found here: https://www.npmjs.com/package/react-native-android-open-settings
import AndroidOpenSettings from 'react-native-android-open-settings'
async goToSettings() {
if(Platform.OS === 'android') {
AndroidOpenSettings.appDetailsSettings()
} else {
Linking.canOpenURL('app-settings:').then(supported => {
if (!supported) {
console.log('Can\'t handle settings url');
} else {
return Linking.openURL('app-settings:');
}
}).catch(err => console.error('An error occurred', err));
}
}
The expected result is for it to open the permissions page and not the app info page.

How to redirect to the app store from a deep link if the app is not installed?

I'd like for users to be able to share a link (e.g. app.com/SKFLA - this is primarily because deep links on their own aren't clickable) via Facebook etc. When clicked, this redirects to a deep link app://SKFLA. If the app is installed, this opens the app - this is all working fine so far. But if the app isn't installed, I'd like to open the app store on the relevant page. Is this achievable? Thanks!
You need UNIVERSAL LINKS
Please check
IOS https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html
Android
https://developer.android.com/training/app-links/
It might also require some extra server-side setup.
Not sure about native behavior.
We used third-party service like https://branch.io/deepviews/.
There is a bunch of similar services.
If someone is still stuck in this issue and needs easiest solution, you will love node-deeplink
1.) If app is installed: Calling an app through deep linking will always call componentDidMount of root component. So you can attach a listener there. Like:
Linking.getInitialURL()
.then(url => {
if (url) {
this.handleOpenURL({ url });
}
})
.catch(console.error);
Linking.addEventListener('url', this.handleOpenURL);
handleOpenURL(event) {
if (event) {
console.log('event = ', event);
const url = event.url;
const route = url.replace(/.*?:\/\//g, '');
console.log('route = ', route);
if(route.match(/\/([^\/]+)\/?$/)) {
const id = route.match(/\/([^\/]+)\/?$/)[1];
const routeName = route.split('/')[0];
if (routeName === 'privatealbum') {
Actions.privateAlbum({ albumId: id });
}
}
}
}
2.) If app is not installed: Just set up a route in your server and node-deeplink package will handle the bridging between web browser to app store when a app is not installed in your mobile.
By this, both the cases will be handled without any struggle

React-native open another app

I have a button and I want to open a facebook page in the facebook app. I can use this solution to open the link in a browser but I'm looking for a better solution that opens faecbook app and my desire page. Is this generally possible? How?
This may not be possible on Android but to do so you follow essentially the same instructions for linking, you just need to swap out http with fb (or the appropriate app id). This SO answer has a bit more information on what may or may not be possible.
Assuming it is possible, to open the facebook app to a profile it would look something like this
const pageId = 'abc123'
Linking.openURL(`fb://profile/${pageId}`)
.catch(err => console.error('An error occurred', err));
Notice that rather than using http I'm using fb
Same as solution of #Spencer answered, but using page instead profile to open fanpage.
<Button
title="Go to Facebook page"
onPress={() => {
const FANPAGE_ID = 'xxxxxxxxxxxxxxxxx'
const FANPAGE_URL_FOR_APP = `fb://page/${FANPAGE_ID}`
const FANPAGE_URL_FOR_BROWSER = `https://fb.com/${FANPAGE_ID}`
Linking.canOpenURL(FANPAGE_URL_FOR_APP)
.then((supported) => {
if (!supported) {
Linking.openURL(FANPAGE_URL_FOR_BROWSER)
} else {
Linking.openURL(FANPAGE_URL_FOR_APP)
})
.catch(err => console.error('An error occurred', err))
}}
/>
Note: You MUST use fanpage ID, not fanpage slug name. If you don't know how to get id, just open your fanpage in browser, view source and find page_id param.
A mix of answers from #Spencer and #Thành worked for me on iOS.
So I settled for just attempting to open the Facebook app link, and then if that fails I fall back to the web browser link, like so:
import { Linking } from "react-native";
const openFacebookLink = facebookId => {
const FANPAGE_URL_FOR_APP = `fb://profile/${facebookId}`;
const FANPAGE_URL_FOR_BROWSER = `https://fb.com/${facebookId}`;
Linking.canOpenURL(FANPAGE_URL_FOR_APP)
.then(appSupported => {
if (appSupported) {
console.log(`Can handle native url: ${FANPAGE_URL_FOR_APP}`);
return Linking.openURL(FANPAGE_URL_FOR_APP);
} else {
console.log(
`Can't handle native url ${FANPAGE_URL_FOR_APP} defaulting to web URL ${FANPAGE_URL_FOR_BROWSER}`
);
return Linking.canOpenURL(FANPAGE_URL_FOR_BROWSER).then(
webSupported => {
if (webSupported) {
console.log(`Can handle web url: ${FANPAGE_URL_FOR_BROWSER}`);
return Linking.openURL(FANPAGE_URL_FOR_BROWSER);
}
return null;
}
);
}
})
.catch(err => console.error("An error occurred", err));
};
Note: the appSupported variable here will always return false until you've edited/added the LSApplicationQueriesSchemes value in your info.plist file. You'll find this file in the ios/yourappname sub-folder of your project. Here are the lines that I added to mine:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fb</string>
<string>fbapi</string>
<string>fb-messenger-api</string>
<string>fbauth2</string>
<string>fbshareextension</string>
</array>
NB: if you're using Create React Native App and/or Expo then you won't be able to edit this file. I abandoned Expo for this reason.
That works on iOS for me, but Android opens it in the browser every time. I've read that Android handles this stuff completely differently to iOS, so I'm not sure if there's any easy solution there.

jquerymobile href links not visibiel with phonegap on android and iOS

I am building a phonegap based mobileweb application which is built almost completely with jquerymobile.
In couple of pages, there are links to external sites and the client wants to show a popup asking if the user is willing to leave the app and go to the external site. Client also asked that the application shall exit if the user chooses to follow the link.
Here is my JS:
if (typeof CORP == "undefined" || !CORP) {
var CORP = {};
}
(function() {
CORP.mk = {
var mobile = false;
init: function() {
$("[data-role='page']").on("pagebeforeshow", CORP.mk.setHandlers);
},
onDeviceReady: function () {
CORP.mk.mobile = true;
document.addEventListener("pause", CORP.mk.onPause, false);
},
onPause: function () { // Exit the app if it goes to background
navigator.app.exitApp();
},
setHandlers: function () {
if ( CORP.mk.mobile ) {
$("a[rel='external']").click(function (e) {
e.preventDefault();
CORP.mk.externalLink = $(this).attr("href");
alert(CORP.mk.externalLink);
navigator.notification.confirm("You are about to close this mobile app and open your web browser.",
CORP.mk.popupConfirm,
"Close This App?");
});
}
}
};
})():
$(document).bind("pageinit", CORP.mk.init);
document.addEventListener("deviceready", CORP.mk.onDeviceReady, false);
Here is my markup:
<a rel='external' data-ajax='false' href='http://www.google.com'>googly</a>
Problem: I tested this code ignoring CORP.mk.mobile on chrome desktop browser and it works fine. However $(this).attr("href"); always returns '#' for the href in Android or IOS and I cannot launch external application with phonegap. I want to be able to get the actual link and launch external app. I tried many combinations and couldn't find a solution. Appreciate any insights.
Finally found the issue. This is happening due to e.preventDefault(); which is replacing the link. I am surprised that this doesn't happen in desktop browsers..
So, instead of
e.preventDefault();
CORP.mk.externalLink = $(this).attr("href");
I need to do
CORP.mk.externalLink = $(this).attr("href");
e.preventDefault();

Categories

Resources