Implementing a plugin (custom intent)

KristerRenaudKristerRenaud SEMember
edited October 2012 in Xamarin.Android

Hello,

I'm trying to write a plug-in for a Java-application and I cannot get the Java-application to call my MfA plug-in.

Perhaps someone here could tell me what I've done wrong when trying to port an example plug-in from Java to Mono for Android? The documentation on the IntentFilter attribute isn't 100% clear.

Here is the AndroidManifest.xml from the Java plug-in:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.MyIntentTest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MyActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.example.MyIntentTest.TestIntent" />
            </intent-filter>
        </activity>
</manifest>

//===============================================================================

Here is an excerpt from the Java plug-in class:


package com.example.MyIntentTest;
...
public class MyActivity extends Activity {
}

//===============================================================================

My Mono AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    android:installLocation="internalOnly" 
    package="com.example.MyIntentTest" 
    android:versionCode="1" 
    android:versionName="0.1.1">

  <uses-sdk android:targetSdkVersion="8" />

  <application android:label="Test Add-in">
  </application>
</manifest>

//===============================================================================

My Mono class:

namespace com.example.MyIntentTest
{
    [Activity(Label = "Test Add-In")]
    [IntentFilter(new[] {"com.example.MyIntentTest.TestIntent"})]
    public class MyActivity : Activity
    {
    }
}

Best Answer

  • JonathanPryorJonathanPryor Xamurai US Xamurai
    Accepted Answer

    Mono for Android will lowercase your namespace name to generate the package name in Android Callable Wrappers. This is why it's handy to view the generated obj\Debug\android\AndroidManifest.xml, to see what types are generated. I suspect that your resulting AndroidManifest.xml will contain:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
        android:installLocation="internalOnly" 
        package="com.example.MyIntentTest" 
        android:versionCode="1" 
        android:versionName="0.1.1">
    
      <uses-sdk android:targetSdkVersion="8" />
    
      <application android:label="Test Add-in">
        <activity android:name="com.example.myintenttest.MyActivity">
          <intent-filter>
             <action android:name="com.example.MyIntentTest.TestIntent" />
          </intent-filter>
        </activity>
      </application>
    </manifest>
    

    Note that the package-name part of //application/activity has been lowercased.

    to enable a plug-in I have to specify "Intentpackage", "Intentclass" and "Intentaction"

    What do you mean that you need to specify those values? How do you specify them? I would have thought that the Java class would do:

    Intent intent = new Intent("com.example.MyIntentTest.TestIntent");
    // ...
    

    that is, the Java intent would contain the //intent-filter/action/@android:name value, which would allow it to find your <activity/>.

    If you need to specify your Java package and class name name in addition to the intent's action, you should look that up at runtime:

    using (var c = Java.Lang.Class.FromType(typeof(MyActivity))) {
        var package   = c.Package.Name;
        var className = c.SimpleName;
    }
    

    Alternatively, if you must use a particular Java name, you can use the ActivityAttribute.Name property:

    [Activity(Label = "Test Add-In", Name="com/example/MyIntentTest/MyActivity")]
    [IntentFilter(new[] {"com.example.MyIntentTest.TestIntent"})]
    public class MyActivity : Activity
    {
    }
    

    Note that explicitly specifying your own type name can result in additional runtime overhead when looking up types, and can result in obscure packaging errors if (when) some types are skipped from the packaging process.

Answers

  • JonathanPryorJonathanPryor Xamurai USXamarin Team Xamurai

    I cannot get the Java-application to call my MfA plug-in.

    Two bits of information would be nice:

    1. How does the Java application call your MfA plug-in? What Intent arguments does it use, etc.?
    2. What is your resulting AndroidManifest.xml? Check e.g. obj\Debug\android\AndroidManifest.xml for the AndroidManifest.xml which is compiled into your .apk.

    Depending on the Android version you're running under, you may also need to run your MfA app before your Java app is able to "see" it/use it. At least, that's the case with broadcast messages: an app can only receive broadcasts if the user has previously launched an Activity from the app.

  • KristerRenaudKristerRenaud SEMember
    edited October 2012

    Hi Jonathan,

    I do not know the specifics. But, to enable a plug-in I have to specify "Intentpackage", "Intentclass" and "Intentaction" and in Logcat I see that ActivityManager logs an attempt to start the activity followed by an ActivityNotFoundException.

    It seems like the C# namespace has been converted to lowercase in the AndroidManifest.xml. That may be the problem, I will try to specify com.example.myintenttest.* instead tomorrow when I'm sitting at the hardware.

    Thanks for your reply.

  • JonathanPryorJonathanPryor Xamurai USXamarin Team Xamurai
    Accepted Answer

    Mono for Android will lowercase your namespace name to generate the package name in Android Callable Wrappers. This is why it's handy to view the generated obj\Debug\android\AndroidManifest.xml, to see what types are generated. I suspect that your resulting AndroidManifest.xml will contain:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
        android:installLocation="internalOnly" 
        package="com.example.MyIntentTest" 
        android:versionCode="1" 
        android:versionName="0.1.1">
    
      <uses-sdk android:targetSdkVersion="8" />
    
      <application android:label="Test Add-in">
        <activity android:name="com.example.myintenttest.MyActivity">
          <intent-filter>
             <action android:name="com.example.MyIntentTest.TestIntent" />
          </intent-filter>
        </activity>
      </application>
    </manifest>
    

    Note that the package-name part of //application/activity has been lowercased.

    to enable a plug-in I have to specify "Intentpackage", "Intentclass" and "Intentaction"

    What do you mean that you need to specify those values? How do you specify them? I would have thought that the Java class would do:

    Intent intent = new Intent("com.example.MyIntentTest.TestIntent");
    // ...
    

    that is, the Java intent would contain the //intent-filter/action/@android:name value, which would allow it to find your <activity/>.

    If you need to specify your Java package and class name name in addition to the intent's action, you should look that up at runtime:

    using (var c = Java.Lang.Class.FromType(typeof(MyActivity))) {
        var package   = c.Package.Name;
        var className = c.SimpleName;
    }
    

    Alternatively, if you must use a particular Java name, you can use the ActivityAttribute.Name property:

    [Activity(Label = "Test Add-In", Name="com/example/MyIntentTest/MyActivity")]
    [IntentFilter(new[] {"com.example.MyIntentTest.TestIntent"})]
    public class MyActivity : Activity
    {
    }
    

    Note that explicitly specifying your own type name can result in additional runtime overhead when looking up types, and can result in obscure packaging errors if (when) some types are skipped from the packaging process.

  • KristerRenaudKristerRenaud SEMember

    I'm working against an Android-based appliance and it has a DB-table for configuring plug-ins. The DB-table has the fields intentpackage, intentclass and intentaction. What exactly happens inside the main application when it calls a plug-in is unknown to me. I've managed to get Java-based plug-ins to work, but as I need to call several .NET libraries I'd like to have a 100% MfA solution.

    Thanks a lot for your tips and I'll report the results tomorrow.

  • KristerRenaudKristerRenaud SEMember

    Update: It was the changing of namespace case that was causing the problem. A little bit unexpected, but thank you Jonathan for your help.

Sign In or Register to comment.