Trevor
Trevor

Reputation: 320

Launch Xamarin Android App Via a Browser Using Custom URL Scheme

Procedure

I have a cross-platform Xamarin project that needs a URL scheme. To enable this feature on Xamarin Android, I add following code in AndroidManifest.xml file, referenced to launch custom android application post.

<activity android:name=".MainActivity">
    <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:scheme="urlschemetest" android:host="testurl" />
    </intent-filter>
</activity>

Typing urlschemetest://testurl directly to a browser in an Android would go to Google search instead of launching the app. So, I have a simple html page that has a hyperlink to open the app, test url scheme.

<a href="urlschemetest://testurl">open app</a>

Problem

By clicking on the hyperlink above, the app isn't launched. Visual Studio shows a Unhandled Exception error in the Output

Java.Lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.companyname.UrlSchemeTest/com.companyname.UrlSchemeTest.MainActivity}: java.lang.ClassNotFoundException: Didn't find class "com.companyname.UrlSchemeTest.MainActivity" on path: DexPathList[[zip file "/data/app/com.companyname.UrlSchemeTest-1/base.apk"],nativeLibraryDirectories=[/data/app/com.companyname.UrlSchemeTest-1/lib/arm, /data/app/com.companyname.UrlSchemeTest-1/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]

Attempted Solutions

1) I looked at attributes in AndroidManifest.xml, and maybe it's because package attribute did not match with namespace used in MainActivity.cs. So I changed it.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   android:versionCode="1" 
   android:versionName="1.0" 
   package="UrlSchemeTest.Droid" 
   android:installLocation="auto">

MainActivity.cs

namespace UrlSchemeTest.Droid
{
    [Activity(Label = "UrlSchemeTest", Icon = "@mipmap/icon", 
        Theme = "@style/MainTheme", MainLauncher = true, 
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity :global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
         //body here
    }
}

2) Using absolute classname in <activity> tag

<activity android:name="UrlSchemeTest.Droid.MainActivity">

3) To see if this was a emulator bug, I've tested the app on both Android emulator and real Android device.

Note: This question is about making it work on Android platform although I've created this Xamarin simple demo on Android and iOS.

Upvotes: 4

Views: 7846

Answers (1)

Trevor
Trevor

Reputation: 320

Explanation

Initially, I thought that the activity name is a combination of the namespace (or package name) and the class name. It's true with native Android and prior to Xamarin Android 5.1. It's not true with Xamarin Android 5.1 and later, as stated in docs

Beginning with Xamarin.Android 5.1, the type name of an activity is based on the MD5SUM of the assembly-qualified name of the type being exported.

With my Xamarin simple demo app, if you build it and open AndroidManifest.xml file in UrlSchemeTest.Android/obj/Debug/android folder, you would see something as below

<activity 
    android:configChanges="orientation|screenSize" 
    android:icon="@mipmap/icon" android:label="UrlSchemeTest" 
    android:theme="@style/MainTheme" 
    android:name="md56cd34387ec86a2e1e9ba0754e1c30e2c.MainActivity">

As you can see, the activity name is a combination of md5 sum and class name.

For this reason, there my had a conflict at build time when I created an activity in AndroidManifest.xml

Solution

First, I need to remove the activity in AndroidManifest.xml. In the demo, following code needs to be deleted

<activity android:name=".MainActivity">
    <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:scheme="urlschemetest" android:host="testurl" />
    </intent-filter>
</activity>

Then, add a IntentFilter property in MainActivity.cs

using Android.App;
using Android.Content;

[IntentFilter(new[] { Intent.ActionView },
    Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
    DataScheme = "urlschemetest",
    DataHost = "testurl")]
public class MainActivity :global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        //code here
    }
}

Url Scheme should work now.

As an advice, I think we should try not to modify AndroidManifest.xml if we can.

Xamarin.Android helps to minimize this difficulty by allowing you to add custom attributes to your classes, which will then be used to automatically generate the manifest for you. Our goal is that 99% of our users should never need to manually modify AndroidManifest.xml.

Upvotes: 10

Related Questions