Deeplink solution for IOS and Android apps works in Facebook - facebook

There are too much Deep Linking (Universal Links or App Links) tutorials. But most of them shows how to enable it in Android or IOS Apps. Also there are paid cloud solutions but they offer to much features. But there are three main problems I faced in real life:
Some browsers doesn’t allow App Links to work. For example you can configure http://example.com to be caught in app, but if this link is clicked by user through Facebook app it is not handled, and Facebook browser shows the web site.
There is no unique standard solution to handle links both for Android and IOS apps.
No practical solution if the App is not installed on mobile device and user clicks an App Link.
I wrote this Q&A which is the result of my studies (spent too many hours) to have a unique and working for all cases solution.
The codes are coming from my working solution, but I removed some parts just to show the idea. If there are some compiling problems, please follow the algorithm and write your own code
Here is the solution, go step by step even if you know some steps, because there are tricks in codes. Also some explanations are in comment lines of the code parts, please read them.
Examples are to handle deeplink http://example.com/v/ by your Android and IOS apps with an argument at the end, for example http://example.com/v/id-of-user?key=value.
1. Configuring Android
1.1 Add activity information to your AndroidManifest.xml file:
<activity
android:name=".appLinkHandlerActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="example.com"
android:pathPrefix="/v/"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!—this intent is needed to handle links to myapp://share, I will explain later why we need it -->
<data
android:host="share"
android:scheme="myapp" />
</intent-filter>
1.2 Create an activity named appLinkHandlerActivity which will handle the links clicked
public class appLinkHandlerActivity extends AppCompatActivity {
/* assume that user is clicked http://example.com/v/my-user-id
actCode will be “v”, pCode will be “my-user-id” */
String actCode="", pCode="";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ATTENTION: This was auto-generated to handle app links.
Intent appLinkIntent = getIntent();
String appLinkAction = appLinkIntent.getAction();
Uri appLinkData = appLinkIntent.getData();
String code = null;
try {
code = getIntent().getData().getLastPathSegment();
} catch (Exception e) {
}
if (code == null) {
Intent i = new Intent(this, {your main activity.class});
startActivity(i);
}
List<String> params=appLinkData.getPathSegments();
if (params.size()>0)
actCode=params.get(0);
if (params.size()>=2)
pCode=params.get(1);
/* assume that user is clicked http://example.com/v/my-user-id actCode is “v”, pCode is “my-user-id” Do now whatever you need. */
}
}
2. Configuring IOS
This is more complex than Android, I will explain the necessary points here. Please refer to documents:
https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1
https://www.raywenderlich.com/128948/universal-links-make-connection
2.1 You have to enable Associated Domains while creating an App ID on Apple Developer Portal. Important issue: you need to have a purchased Apple Developer Account to enable this option, that means without purchasing a developer account, you can’t try AppLinks on your IOS project.
2.2 In XCode project, open “Capabilites” tab and switch Associated Domains to On. If you didn’t enable Associated Domains in Apple Developer Portal App ID section, this might not be selectable
Add an entitlement by clicking on + button under Associated Domains option, write “applinks:example.com”.
2.3 Create a file on your web server named “apple-app-site-association” and this file must be accessed through https://example.com/apple-app-site-association HTTPS is mandatory and if it is not a valid SSL certificate App Link might not work. Add following lines into apple-app-site-association file:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "6HY7TF56.com.example.app",
"paths": [ "/ios/*", "/v/*" ]
}
]
}
}
appID is format of {“Team ID”.”Bundle ID of your App”}. You can find your teamID under Membership Details section at Developer Portal.
We handle the link http://example.com/v/parameters, but here you see there is another path configuration for “/ios/*”. It is needed to bypass unsupported browsers, I will explain later.
2.4 In AppDelegate.m file add two methods to handle the user clicks on example.com
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{
if ([userActivity.activityType isEqualToString: NSUserActivityTypeBrowsingWeb]) {
NSURL *url = userActivity.webpageURL;
[self parseUrl:url];
}
return YES;
}
- (void) parseUrl:(NSURL * )handledUrl {
NSString *urlStr=#"";
NSString *pCode=#"";
NSString *handledUrlStr=[handledUrl parameterString];
NSString *handledUrlQueryPart;
NSArray<NSString *> *pathsArray= [handledUrl pathComponents];
//remember that we only added paths “/v/*” and “/ios/*” to handle in apple-app-site-association file. If you want to handle more subpaths you can add them into apple-app-site-association file, then below if-else conditions. Don’t touch to config and code for “/ios/*” path, it is needed to bypass unsopported browsers.
if ([pathsArray[1] isEqual: #"v"]){
//sample url= http://example.com/v/menu?aaa=bbb
pCode = pathsArray[2];
handledUrlQueryPart=[handledUrl query];
} else if ([pathsArray[1] isEqual: #"ios"]){
//sample url= http://example.com/ios/deeplink-ios.php?/v/menu?aaa=bbb
NSArray *uriArray = [[handledUrl query] componentsSeparatedByString:#"?"];
NSArray *queryPathsArray = [uriArray[0] componentsSeparatedByString:#"/"];
if ([queryPathsArray count] > 2)
pCode = queryPathsArray[2];
if ([uriArray count] > 1 ){
handledUrlQueryPart=uriArray[1];
}
}
/* here pCode is the parameter what is passed from user. If the link clicked is http://example.com/v/menu it is “menu”. If the link clicked is http://example.com/v/menu?aaa=bbb it is “menu?aaa=bbb”. So you can do now what ever you need. */
}
3. Managing the unacught clicks.
3.1 Ok, Your android and IOS apps should handle the clicks on link http://example.com/v/blabla and pass the “blabla” parameter to pCode variable used in the methods I showed. But some browsers like Facebook app may disable App Links to work. In this case user click goes to your web server and the browser tries to show the content of http://example.com/v/blabla which is probably 404 Page Not Found. To handle these clicks we will configure Apache web server and redirect users to your App. If you use IIS or another, I don’t know how to do it, but you can take this as sample and use same algortihm to configure your web server.
3.2 Write the below lines in .htaccess file in root directory of example.com
#redirect to deeplink
<IfModule mod_rewrite.c>
#if there is a request to example.com/v/any-sub-path, redirect them to example.com/deeplink.php file. This rule is for both IOS and Android
RewriteRule ^(v)/.* /deeplink.php [L]
#if there is a request to example.com/ios/any-sub-path, redirect them to app installation page. That means your app is not installed on IOS device. This rule is for IOS devices only
RewriteRule ^(ios)/.* http://itunes.apple.com/install-of-your-app [L]
</IfModule>
4. Redirect users to Apps
4.1 The redirection rules in .htaccess file shown at step-3 redirects users to deeplink.php file. So here is the content of that file to redirect users to your App.
<?php
$request_uri=$_SERVER[REQUEST_URI];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
if(stripos($ua,'android') == true){
//if user device is android, redirect it to intent url which is handled by Android.
$redir_tag="<meta http-equiv='refresh' content='0;url=intent://share$request_uri/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;package=com.example.app;end' />";
//scheme=myapp and host named “share” is configured in AndroidManifest.xml file to be handled by the activity.
//fallback url is the url, if your app is not installed on android device, so you can redirect them to a page to install android app. In some cases users are redirected to Play Store directly for application id of com.example.app
}
else if ( (stripos($ua,'iPhone') == true) || (stripos($ua,'iPad') == true) ) {
//if user device is IOS, redirect them to a web page to see. There will be a link here to the another handled link: http://example.com/ios/blabla.
// due to my experience there is no way to redirect IOS to app directly at this stage, user must click a link on browser and that link must be different than the link which was shown and clicked at first.
// another experience taught me ther ecan be problems if we redirect users to a web page under example.com which is configured as applink, so I redirect users to a page under deep.example.com here
$redir_tag="<meta http-equiv='refresh' content='0;url=http://deep.example.com/deeplink-ios.php?$request_uri' />";
}
else {
//If the device is neither IOS nor Android, redirect users to a web page which gives information that this link is for Android and IOS only
$redir_tag="<meta http-equiv='refresh' content='0;url=http://example.com/non-mobile' />";
}
?>
<html>
<head>
<!— add tags for no-caching, this is important, the date below is my son’s birth time and date, he is now 6, so you can use it as a date in the past -->
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="-1" />
<meta http-equiv="expires" content="Tue, 31 May 2011 10:15:00 GMT+3" />
<meta http-equiv="pragma" content="no-cache" />
<?php echo $redir_tag; ?>
</head>
</html>
5. Show IOS users a link to click
5.1 Here is the content of http://deep.example.com/deeplink-ios.php file. Users will see a page like below, when they clicked on the link, that request should be handled by your IOS app.
<?php
//we create a link to http://example.com/ios/… which is configure to be handled by IOS app. IOS needs to be a user click in some cases to handle the request, that is why this page is shown to the user
$request_uri=$_SERVER[REQUEST_URI];
$link="<div class='w3-container w3-card'><h1><a href='http://example.com/ios$request_uri'>Click here to open MyApp</a></h1></div>";
?>
<html>
<head>
<!—adding no-cache tags is also important here-->
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="-1" />
<meta http-equiv="expires" content="Tue, 31 May 2011 10:15:00 GMT+3" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3mobile.css">
</head>
<body>
<?php echo $link ?>
</body>
</html>
6. Case Analysis:
6.1 Android
6.1.1 Case-1 – app is installed on device
• Browser requests for http://example.com/v/blabla
• Android catches the link and create the activity configured in your manifest file
6.1.2 Case-2 - app is installed on device
• Browser requests for http://example.com/v/blabla
• Android can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is android, and redirect to: intent://share/v/blabla/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;package=com.example.app
• Android catches the request which is for intent://, so due to configuration in manifest file myapp://share/v/blabla is handled by the activity
6.1.3 Case-3 - app is not installed
• Browser requests for http://example.com/v/blabla
• Android can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is android, and redirect to: intent://share/v/blabla/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;package=com.example.app
• Android catches the request which is for intent://, but there is no app installed for id: com.example.app. It falbacks and redirect browser to http://example.com/get-app or Play Store installation page of your app in some cases
6.2 IOS
6.2.1 Case-1 – app is installed on device
• Browser requests for http://example.com/v/blabla
• IOS catches the link and call the continueUserActivity method in AppDelegate.m
6.2.2 Case-2 – app is installed on device
• Browser requests for http://example.com/v/blabla
• IOS can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is IOS, and redirects to: http://deep.example.com/deeplink-ios.php?/v/blabla
• deeplink-ios.php file shows a URL to user. URL is: http://lify.me/ios/v/blabla
• User clicks the URL, and browser requests for http://lify.me/ios/v/blabla
• IOS catches the request due to configuration for path “/ios/*” in apple-app-site-association file and call the continueUserActivity method in AppDelegate.m
• If IOS can’t catch the request for http://lify.me/ios/v/blabla with any reason, it will behave as app is not installed on device. See that case.
6.2.3 Case-2 – app is not installed on device
• Browser requests for http://example.com/v/blabla
• IOS can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is IOS, and redirects to: http://deep.example.com/deeplink-ios.php?/v/blabla
• deeplink-ios.php file shows a URL to user. URL is: http://lify.me/ios/v/blabla
• User clicks the URL, and browser requests for http://lify.me/ios/v/blabla
• If IOS can’t catch the request for http://lify.me/ios/v/blabla
• Browser connects to web server, request for /ios/v/blabla
• It is redirected to http://itunes.apple.com/install-of-your-app due to configuration in .htaccess file on web server
6.3 App Link is clicked on non Android or IOS device
• Browser requests for http://example.com/v/blabla
• Device OS can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is nor IOS neither Android, and redirects to: http://example.com/non-mobile

Related

DOMException: Could not start audio source

I'm writing a React App and I'm using Web Speech API for both speaking and voice recognition. I've written some code that works on desktop Chrome.
The way I ask for permission from the user is the following:
navigator.mediaDevices
.getUserMedia({ audio: true })
.then(function (stream) {
console.log('Succeeded');
/*Handle success*/
})
.catch(function (err) {
console.log('Failed: ', err)
/*Handle failure*/
});
However, this same code always goes to the catch callback when used from my Android phone, with the following written to the console:
Failed: DOMException: Could not start audio source
The site asks me if I want to allow microphone usage and when I go into the site settings, it says microphone and sound are both allowed, which makes me believe I've done the user interaction part well.
The site is served over HTTPS (already solved that issue), so I believe this is not an issue as well.
I'm using Xiaomi Mi 9 with Chrome Chrome 86.0.4240.185 in this test.
My first instinct is that I'm doing something wrong here? If this is the case, what?
Otherwise, what can I do to fix this issue?
Check if your Android app has required permissions:
android.permission.RECORD_AUDIO
android.permission.MODIFY_AUDIO_SETTINGS (might be optional)
You can check html or ejs file ;
you have to use in the body tags
Such as:
body
</div>
<script src="script.js"></script>
/body

cordova-plugin-appavailability is not detecting facebook app

I am using the AppAvailability cordova plugin to check if the facebook app is installed on a device from my own app.
In order to test its working, I have the facebook app installed on my iOS device. the appAvailability.check() should therefore execute the success function however it is executing the error callback function instead. Can you help? (I am unable to post the issue on the github page as issues are closed.)
I have this in the config.xml
<plugin name="cordova-plugin-appavailability" spec="0.4.2" source="npm" />
and in my javascript i have the following:
var scheme = "fb://";
appAvailability.check(
scheme, // URI Scheme or Package Name
function() { // Success callback
alert(scheme + ' is available :)');
},
function() { // Error callback
//this is being executed even when the fb app IS installed.
alert(scheme + ' is not available :(');
}
);
Sample I'm showing is for ios. Make sure you edit the plist (platforms/ios/appname/appname-Info.plist) file for your app and add facebook.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>facebook</string>
</array>
In your androidmanifest.xml file just add the below code on above or below tag.
<queries>
<package android:name="com.whatsapp" />
<package android:name="com.whatsapp.w4b" />
</queries>

Appcelerator Facebook module doesn't fire login event

Appcelerator 5.2.0 Facebook Module (Android) doesn't fire 'login' event after successful logged in.
What could it be?
var FB = require('facebook');
FB.initialize();
FB.permissions = ['public_profile', 'email', 'user_events'];
FB.forceDialogAuth = false;
FB.addEventListener('login', function() {
alert('login');
});
FB.authorize();
Thanks!
Hello Oxana,
Additional Android Setup Steps
Since Facebook module v4.0.0, for the Android platform, you need to:
Add the Facebook Login activity to the Android manifest
Add the Facebook App ID to the Android resources string.xml file
Create a Facebook proxy and associate it with the current active activity (you need)
Manifest:
<ti:app>
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
<application>
<activity android:label="#string/app_name"
android:name="com.facebook.LoginActivity" android:theme="#android:style/Theme.Translucent.NoTitleBar"/>
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="#string/facebook_app_id"/>
</application>
</manifest>
</android>
<ti:app>
Add the Facebook App ID to Android Resources
Add a string element to the /platform/android/res/values/strings.xml file with the name attribute set to facebook_app_id and the node text set to your Facebook App ID. Create the file if it does not exist.
<resources>
<string name="facebook_app_id">FACEBOOK_APP_ID</string>
</resources>
**I think this step is that you need =D **
Create a Facebook Proxy
Use the createActivityWorker() method to create a Facebook proxy. Pass the method a dictionary with the lifecycleContainer property set to the current active instance of a standalone Window (window not contained in a tab group) or TabGroup. Create the proxy before calling the open() method on either the window or tab group.
The Facebook module needs to hook into the lifecycle events of the current active activity in order to synchronize its state between various activities in the application, for example, to update the label of the Login button when the user logs in or out of Facebook.
Attach the proxy to the Window or TabGroup object, so it does not get garbage collected.
win.fbProxy = fb.createActivityWorker({lifecycleContainer: win});
Documentation: Facebook module

Chrome packaged application - downloading file from Webview

I am working on getting an existing Ajax style web application functional as a Chrome packaged app. The Ajax app is running in a Webview inside the packaged app and is mostly working great.
The Ajax app allows users to upload documents using standard HTML 5 upload and drag/drop. Uploads work fine but downloads are not working.
This is a simplified sample of the logic in the Ajax app. It uses Javascript to handle the click event, performs some logic, and then eventually triggers the download by setting the location of a hidden IFrame.
<html>
<head>
<script type="text/javascript">
function downloadFile(url) {
window.frames['dataTransfer'].location = url;
}
</script>
</head>
<body>
Google<br/>
Download PDF<br/>
Download PDF JS<br/>
<iframe name="dataTransfer" style="width: 0; height: 0; border: none;"></iframe><br/>
</body>
</html>
If you run this in a standard Chrome tab all 3 links work fine. However in a Chrome App only the first 2 links work - clicking the 3rd link does nothing. In the network section of the Developer tools it actually appears to start the download but then is quickly cancelled.
The manifest of the Chrome app allows Webview and the relevant domains involved.
{
"manifest_version": 2,
"name": "Test Download",
"version": "0.1.0",
"permissions": [
"webview",
"<DOMAIN OF THE SAMPLE PAGE ABOVE>",
"https://s3.amazonaws.com/"
],
"app": {
"background": {
"scripts": ["background.js"]
}
},
"icons": {},
"minimum_chrome_version": "28"
}
The Chrome App has some simple newwindow handling in it as well.
window.onload = function() {
var webview = document.querySelector('#app-webview');
webview.src = '<URL TO SAMPLE PAGE ABOVE>';
webview.addEventListener('newwindow', function(e) {
e.preventDefault();
e.stopPropagation();
window.open(e.targetUrl);
});
};
Any suggestions on how to get downloads working (hopefully without requiring significant changes to the Ajax app)?
File downloads from the webview guest are gated by a permissionrequest event per download attempt. The default response is to deny download permission.
Note: The download permissionrequest will not land until Chrome 30 now, and it appears that documentation has not yet been made available. It is generally a stable API though, and it is unlikely to change between now and then.
In order to override this behavior, you need to handle the event and explicitly allow the download to happen. As an example:
var webview = document.querySelector('#app-webview');
webview.addEventListener('permissionrequest', function(e) {
if (e.permission === 'download') {
e.request.allow();
}
});
The event includes additional information (such as the download URL in e.url) in case you want to further filter your grants.
Be aware that this will only permit the download to happen using the regular Chrome file download experience, which isn't necessarily what you'll want from within a packaged app. Your options are limited until the chrome.downloads API is made available to apps.
One possibility is to deny the download request, grab the URL from the event, and manually manage the download process with XHR, the fileSystem API, and whatever UX you want to build.

How to redirect from Mobile Safari to Native iOS app (like Quora)?

On my iPhone, I just noticed that if I do a Google Search (in Mobile Safari) and select a result on quora.com, the result page launches the native Quora app on my phone.
How is this done? Specifically, is it a detection of the user agent and the use of an iOS URL scheme? Can it tell if the native app is installed and/or redirect to the app store?
I'm reposting an answer to my own related (but was originally Ruby-on-Rails-specific) question from here: Rails: redirect_to 'myapp://' to call iOS app from mobile safari
You can redirect using javascript window.location.
Sample code:
<html><head>
<script type="text/javascript">
var userAgent = window.navigator.userAgent;
if (userAgent.match(/iPad/i) || userAgent.match(/iPhone/i)) {
window.location = "myiosapp://"
}
</script>
</head>
<body>
Some html page
</body>
</html>
Just a small improvement of the JS code, if the app is not installed, it will send the user to itunes store ;)
<script type="text/javascript">
// detect if safari mobile
function isMobileSafari() {
return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/)
}
//Launch the element in your app if it's already installed on the phone
function LaunchApp(){
window.open("Myapp://TheElementThatIWantToSend","_self");
};
if (isMobileSafari()){
// To avoid the "protocol not supported" alert, fail must open itunes store to dl the app, add a link to your app on the store
var appstorefail = "https://itunes.apple.com/app/Myapp";
var loadedAt = +new Date;
setTimeout(
function(){
if (+new Date - loadedAt < 2000){
window.location = appstorefail;
}
}
,100);
LaunchApp()
}
</script>
You can do trigger your application to be launched using custom URL scheme, registered by your application with the iOS runtime. Then on your website, write code to detect the incoming User-Agent and if iOS is detected generate your custom URL's instead of regular http ones.