Force Android App Link to open in browser - react-native

We have a React Native app which shows our mobile website and adds some extra features.
Since Android 12 App links (like domain.com) always open our app: https://developer.android.com/training/app-links
This behaviour is not always desirable, for example in this scenario:
Customer is logged in and starts an order via their browser
Customer needs to pay via an app from their bank
After payment, the customer is redirected back to our website (domain.com/returnUrl)
Now the app is opened, instead of the browser, so the customer isn't logged-in and isn't allowed to view the page.
In this case, after payment started from the browser, we would like to redirect the customer back to the browser instead of the app.
Is there a way to open a link in the browser (ie. via domain.com/returnUrl?force-browser) instead of the app?
Related: Android App link - Open a url from app in browser without triggering App Link

Based on this answer, I've created a RN Native Module and instead of using await Linking.openURL(url) you can just use the Native Module's exposed method to open Android App links.
I've followed the official RN tutorial to make an Android Native Module.
So in summary, first you will have to create a Java class file inside android/app/src/main/java/com/your-app-name/folder. I've named the module DefaultBrowserModule so the path is src/main/java/com/your-app-name/DefaultBrowserModule.java. Here's how it looks like:
package com.your-app-name;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class DefaultBrowserModule extends ReactContextBaseJavaModule {
private ReactApplicationContext _context;
DefaultBrowserModule(ReactApplicationContext context) {
super(context);
this._context = context;
}
#NonNull
#Override
public String getName() {
return "DefaultBrowserModule";
}
// This is the method that we're exposing
#ReactMethod
public void openUrl(String url) {
Intent defaultBrowser = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER);
defaultBrowser.setData(Uri.parse(url));
// Through ReactApplicationContext's current activty, start a new activity
this._context.getCurrentActivity().startActivity(defaultBrowser);
}
}
After that we'll have to register the module with React Native. That can be done by adding a new Java class file to the android/app/src/main/java/com/your-app-name/ folder. I've named mine DefaultBrowserPackage: src/main/java/com/your-app-name/DefaultBrowserPackage.java:
package com.your-app-name;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DefaultBrowserPackage implements ReactPackage {
#NonNull
#Override
public List<NativeModule> createNativeModules(#NonNull ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new DefaultBrowserModule(reactContext));
return modules;
}
#NonNull
#Override
public List<ViewManager> createViewManagers(#NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
The last step is to register the DefaultBrowserPackage inside of MainApplication.java (android/app/src/main/java/com/your-app-name/MainApplication.java). Locate ReactNativeHost’s getPackages() method and add your package to the packages list
#Override
protected List<ReactPackage> getPackages() {
#SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// below DefaultBrowserPackage is added to the list of packages returned
packages.add(new DefaultBrowserPackage());
return packages;
}
Now we are ready to use it inside of JS. So wherever you want to use it, you can do it like this:
import { Linking, NativeModules, Platform } from 'react-native';
// DefaultBrowserModule should be equal to the return value of the getName() method
// inside of the src/main/java/com/your-app-name/DefaultBrowserModule.java class
const { DefaultBrowserModule } = NativeModules;
export const openUrl = async (url) => {
if (Platform.OS === 'android') {
DefaultBrowserModule.openUrl(url);
} else {
await Linking.openURL(url);
}
};
// And then use it like this
await openUrl('https://my-app-link-domain.com');

Deep and universal linking happens on the operating level and it's hard to control the behavior of other app linking I think it should security breach as some apps try to override the deep link behaviors of another app.
Try to create your simple page with your custom URL https://my-domain.com which redirect to tour target URL without opening associated app.

The best possible solution for that can be using android:pathPattern in android manifest. Basically you have to provide path pattern (a sort regex) to match the valid links.
Documentation for that can be found here.
https://developer.android.com/guide/topics/manifest/data-element

Related

React Native importing React Navigation

I follow all instructions for installing React Navigation for Android.
First, "npm install --save react-navigation", " npm install --save react-native-gesture-handler". "react-native link react-native-gesture-handler" and lastly, updated my MainActivity.java
Here is my MainActivity.java:
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
#Override
protected String getMainComponentName() {
return "InstagramClone";
}
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
#Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this)
}
};
}
}
This is the error I got:
What went wrong:
Failed to create parent directory 'D:\React Native Projects\InstagramClone\node_modules\react-native-gesture-handler\android\build' when creating directory 'D:\React Native Projects\InstagramClone\node_modules\react-native-gesture-handler\android\build\intermediates\check-manifest\debug'
If you initiated your project with expo just install react-native-gesture-handler. But if your projected started by react-native cli you have to first install react-native-gesture-handler and then link it to all packages by the command:
react-native link
if you had to a link react-native-gesture-handler there should be no problem or you try react-native link it will be link all package. Or if you still face same problem delete app first on your emulator then React-native run-android again
You need to install react-native-gesture-handler npm separately. They create separated npm package for touch & gesture handling and recognition.
Step 1.
npm i react-native-gesture-handler
Step 2.
react-native link react-native-gesture-handler
Step 3.(optional )
If step 2 is not worked properly, code is not configured properly so we are manually configure it using step 3
To finalize the installation of react-native-gesture-handler for Android, be sure to make the necessary modifications to MainActivity.java:
import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; import
com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
#Override protected
String getMainComponentName() { return "Example"; }
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
#Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this); }
};
} }
No additional steps are required for iOS.
Please Refer the following document for more information:-
https://reactnavigation.org/docs/en/getting-started.html#installation
https://www.npmjs.com/package/react-native-gesture-handler/v/1.0.0-alpha.34?activeTab=readme
https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html

Execute Cucumber Step Defination with multiple files with Page Object Model

I am trying to execute a Cucumber Feature file which Step Definition in two different files. All methods in first Step Defination is executed and when executing second one, It launches a new browser instance, instead of continuing with existing one.
Cucumber Feature file
Scenario: Given I open Firefox and Navigate to Guru
When I enter UserName and Password and login to guru
And I click on New Customer
Then New Customer Page is displayed
And I click on HomePage
Then HomePage is displayed
First Step Definition
package stepDefination;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
import Pages.HomePage;
import Pages.NewCustomerPage;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
public class GuruStepDef {
WebDriver Driver;
NewCustomerPage customerPage;
HomePage homePage = new HomePage(Driver);
#When("^I enter UserName and Password and login to guru$")
public void I_enter_and_and_login_to_guru() {
homePage=homePage.setup();
homePage.navigateToWebApp();
}
#Then("^HomePage is displayed$")
public void Homepage_is_displayed() {
//assert
}
#Then("^I click on New Customer$")
public void I_click_on_New_Customer() {
customerPage= homePage.NavigateToCustomerPage();
}
#Then("^New Customer Page is displayed$")
public void New_Customer_Page_is_displayed() {
//assert
}
}
Second Step Definition
package stepDefination;
import org.openqa.selenium.WebDriver;
import Pages.HomePage;
import Pages.NewCustomerPage;
import cucumber.api.java.en.Then;
public class SmokeTest {
WebDriver Driver;
NewCustomerPage customerPage;
HomePage homePage = new HomePage(Driver);
#Then("^I click on HomePage$")
public void I_click_on_HomePage() {
homePage=customerPage.Manager();
}
}
In both classes, you have:
HomePage homePage = new HomePage(Driver);
You are creating two instances of HomePage. If you want to utilize the same object, you'll need to share it between the two classes. For example, you could create HomePage in one of the classes and use a getter in the other, or you could use a Singleton pattern in the object itself to ensure only one instance is created at a time.

Whitelist user IPs in Shiro

I would like to enable Facebook to crawl my website, however it needs user authentication. Facebook says one way to get around this is to whitelist their ips. I am using Apache Shiro and I know that you can get client's ip by calling getHost from BasicHttpAuthenticationFilter, however I do not know how to let certain ip addresses past the authentication.
You will likely have to build a custom implementation of Shrio's
org.apache.shiro.web.filter.authc.AuthenticatingFilter
Minimally, you will have to customize BasicHttpAuthenticationFilter by extending it and adding logic to skip the BasicHttpAuthenticationFilter if the request is coming from a whitelisted IP address.
package com.acme.web.filter.authc;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class WhitelistedBasicHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
private Set<String> whitelist = Collections.emptySet();
public void setWhitelist(String list) {
whitelist = new HashSet<String>();
Collections.addAll(whitelist, list.split(",")); //make sure there are no spaces in the string!!!!
}
#Override
protected boolean isEnabled (ServletRequest request, ServletResponse response) throws ServletException, IOException
{
if (whitelist.contains(request.getRemoteAddr())) {
return false;
}
return super.isEnabled(request, response);
}
}
In your 'shiro.ini'
authc=com.acme.web.filter.authc.WhitelistedBasicHttpAuthenticationFilter
authc.whitelist=192.168.1.1,192.168.1.2,192.168.2.3

What is Hybrid Driven and Keyword Driven in selenium?

Please give an example for Hybrid Driven and Keyword Driven using selenium. Thanks in advance!
Never heard about Hybrid driven testing, you might be referring to testing hybrid applications.
A hybrid application (hybrid app) is one that combines elements of
both native and Web applications. Native applications are developed
for a specific platform and installed on a computing device. Web
applications are generalized for multiple platforms and not installed
locally but made available over the Internet through a browser. Hybrid
apps are often mentioned in the context of mobile computing.
About Keyword driven testing, each keyword corresponds to an individual testing action like a mouse click, selection of a menu item, keystrokes, opening or closing a window or other actions. A keyword-driven test is a sequence of operations, in a keyword format, that simulate user actions on the tested application.
Posted below is a simple class for Hybrid (Modular and Data Driven) framework -
SearchData.java
Has the code for data to be used in the test.
package com.data;
public class SearchData {
private String url;
private String searchWord;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getSearchWord() {
return searchWord;
}
public void setSearchWord(String searchWord) {
this.searchWord = searchWord;
}
}
SearchPage.java
Contains modules of code.
package com.page;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import com.data.SearchData;
public class SearchPage {
WebDriver driver;
public SearchPage(WebDriver driver) {
this.driver = driver;
}
public void launchGoogle(SearchData searchData) {
driver.get(searchData.getUrl());
}
public void search(SearchData searchData) {
driver.findElement(By.name("q")).sendKeys(searchData.getSearchWord());
driver.findElement(By.name("btnG")).click();
}
}
GoogleTest.java
Contains the actual Junit Test.
package com.test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import com.data.SearchData;
import com.page.SearchPage;
public class GoogleTest {
protected WebDriver driver;
#Before
public void setUp() {
driver = new FirefoxDriver();
}
#After
public void tearDown() {
driver.close();
driver.quit();
}
#Test
public void searchTest() throws InterruptedException {
// set the data
SearchData searchData = new SearchData();
searchData.setUrl("https://www.google.com");
searchData.setSearchWord("Selenium");
// call the methods
SearchPage searchPage = new SearchPage(driver);
searchPage.launchGoogle(searchData);
searchPage.search(searchData);
Thread.sleep(10000);
}
}
The above code is pretty basic and can be enhanced to a great extend.
As for Keyword driven framework use SELENIUM IDE / ROBOT FRAMEWORK.

Restlet 2.1 application has null context

I am running a JSE application with Restlet 2.1. I am attempting to use the application context, and am finding that it is always null in my application. Because it is null, I cannot seem to access anything -- including any attributes that I pass when I invoke the resource.
The code for the restlet application class is below:
package net.factor3.mailapp;
import net.factor3.mailapp.impl.PageServerImpl;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.MediaType;
import org.restlet.data.Protocol;
import org.restlet.routing.Router;
import org.restlet.routing.Template;
public class MyServer extends Application
{
public MyServer()
{
setName("Test Application");
setDescription("Testing use of Restlets");
}
#Override
public Restlet createInboundRoot()
{
Context ctx = getContext();
Router route = new Router(ctx);
route.setDefaultMatchingMode(Template.MODE_EQUALS);
route.attach("http://localhost:8100/",PageServerImpl.class);
route.attach("http://localhost:8100/{page}",PageServerImpl.class);
return(route);
}
public static void main(String[] args) throws Exception
{
Server asrv = new Server(Protocol.HTTP,8100);
asrv.setNext(new MyServer());
asrv.start();
}
}
Note that the PageServerImpl is a ServerResource. In createInboundRoot(), I use getContext() to get the application's context and put it into ctx. ctx is always null, and I believe for that reason parameters and attributes are lost in the ServerResource.
Is this a bug in the JRE version of Restlet 2.1? If it is, where do I go to report it? There is no clear link to bug reports on the Restlet website.
If it is not a bug, then how do I get a decent context in an application of this kind???
Someone please advise.
Using Component Class you can create statisfy your need:
public class MyServer extends Application
{
public MyServer()
{
setName("Test Application");
setDescription("Testing use of Restlets");
}
public static void main(String[] args) throws Exception
{
// Create a new Restlet component and add a HTTP server connector to it
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
// Then attach it to the local host
component.getDefaultHost().attach("/trace", GenericResource.class);
// Now, let's start the component!
// Note that the HTTP server connector is also automatically started.
component.start();
}
}