Extending AIR for Android

*** The following is totally unsupported by Adobe ***
*** UPDATE: Adobe has officially added native extensions to AIR. I highly recommend you use that approach instead of mine. ***

Adobe AIR provides a consistent platform for desktop and mobile apps. While consistency is very important there are times when developers need to extend beyond the common APIs. This article will walk you through how to integrate AIR for Android applications with other native APIs and functionality in the Android SDK. It covers three common use cases for native extensibility: System Notifications, Widgets, and Application Licensing.

If you’d like to follow along you will need the following prerequisites:

Before getting started, a little background will help. Android applications are distributed as APK files. An APK file contains the Dalvik executable (dex), which will run on an Android device inside the Dalvik VM. The Android SDK compiles a Java-like language to dex.

AIR for Android applications are also distributed as APK files. Inside of these APK files is a small bit of dex that bootstraps the AIR for Android runtime, which then loads and runs the SWF file that is also inside of the APK. The actual dex class that bootstraps the AIR application is dynamically generated by the adt tool in the AIR SDK. The class is named AppEntry and its package name depends on the AIR application ID, but it always begins with “air”. The AppEntry class checks for the existence of the AIR runtime and then launches the AIR application. The Android descriptor file in an AIR APK specifies that the main application class is the AppEntry class.

To extend AIR for Android applications to include native APIs and Android SDK functionality, you start by creating a SWF file using Flex and then copy that SWF file, the dex classes for AIR for Android, and the required resources into a standard Android project. By using the original AppEntry class you can still bootstrap the AIR application in the Android project but you can extend that class to gain a startup hook.

  1. To get started, download a package with the required dependencies for extending AIR for Android:
    http://www.jamesward.com/downloads/extending_air_for_android-flex_4_5-air_2_6-v_1.zip
  2. Next, create a regular Android project in Eclipse (do not create an Activity yet):
  3. Copy all of the files from the zip file you downloaded into the root directory of the newly created Android project. You will need to overwrite the existing files and update the launch configuration (if Eclipse asks you to).
  4. Delete the “res/layout” directory.
  5. Add the airbootstrap.jar file to the project’s build path. You can do that by right-clicking on the file, then select Build Path and then Add to Build Path.
  6. Verify that the project runs. You should see “hello, world” on your Android device. If so, then the AIR application is properly being bootstrapped and the Flex application in assets/app.swf is correctly being run.

    At this point if you do not need any custom startup hooks then you can simply replace the assets/app.swf file with your own SWF file (but it must be named app.swf). If you do need a custom startup hook then simply create a new Java class named “MainApp” that extends the air.app.AppEntry class.

  7. Override the onCreate() method and add your own startup logic before super.onCreate() is called (which loads the AIR app). Here is an example:
    package com.jamesward;
     
    import air.app.AppEntry;
    import android.os.Bundle;
     
    public class MainApp extends AppEntry {
     
    	@Override
    	public void onCreate(Bundle arg0) {
    		System.out.println("test test");
    		super.onCreate(arg0);
    	}
    }
  8. Open the AndroidManifest.xml descriptor file and tell it to use the new MainApp class instead of the original AppEntry class. First change the package to be the same as your MainApp’s package:
    <manifest package="com.jamesward" android:versionCode="1000000" android:versionName="1.0.0"
      xmlns:android="http://schemas.android.com/apk/res/android">

    Also update the activity to use the MainApp class (make sure you have the period before the class name):

    <activity android:name=".MainApp"

    You can also add any other permissions or settings you might need in the AndroidManifest.xml file.

  9. Save the changes and, when Eclipse prompts you, update the launch configuration.
  10. Run the application and you should again see “hello, world”. This time, however, in LogCat (command line tool or Eclipse view) you should see the “test test” output. Now that you have a startup hook, you can do some fun stuff!

System Notifications and Services

AIR for Android applications don’t yet have an API to do Android system notifications. But you can add system notifications to your AIR for Android application through a startup hook. In order for the AIR application to communicate with the native Android APIs you must provide a bridge for the communication. The simplest way to create that bridge is using a network socket. The Android application can listen for data on the socket and then read that data and determine if it needs to display a system notification. Then the AIR application can connect to the socket and send the necessary data. This is a pretty straightforward example but some security (for instance a key exchange) should be implemented to insure that malicious apps don’t discover and abuse the socket. Also some logic to determine which socket should be used would likely be necessary.

  1. Inside the application section add a new Android Service:
  2. <service android:enabled="true" android:name="TestService" />
  3. Since this example uses a Socket you will also need to add the INTERNET permission:
  4. <uses-permission android:name="android.permission.INTERNET"/>
  5. You might also want to enable the phone to vibrate when there is a new notification. If so add that permission as well:
    <uses-permission android:name="android.permission.VIBRATE"/>
  6. Save your changes to AndroidManifest.xml.
  7. Next, create the background Java Service class, called TestService. This service will listen on a socket and when necessary, display an Android Notification:
    package com.jamesward;
     
    import java.io.BufferedInputStream;
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
     
    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.app.Service;
    import android.content.Context;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.Looper;
    import android.util.Log;
     
    public class TestService extends Service
    {
      private boolean stopped=false;
      private Thread serverThread;
      private ServerSocket ss;
     
      @Override
      public IBinder onBind(Intent intent)
      {
        return null;
      }
     
      @Override
      public void onCreate()
      {
        super.onCreate();
     
        Log.d(getClass().getSimpleName(), "onCreate");
     
          serverThread = new Thread(new Runnable() {
     
            public void run()
            {
                    try
                    {
                            Looper.prepare();
                            ss = new ServerSocket(12345);
                            ss.setReuseAddress(true);
                            ss.setPerformancePreferences(100, 100, 1);
                            while (!stopped)
                            {
                                    Socket accept = ss.accept();
                                    accept.setPerformancePreferences(10, 100, 1);
                                    accept.setKeepAlive(true);
     
                                    DataInputStream _in = null;
                                    try
                                    {
                                            _in = new DataInputStream(new BufferedInputStream(accept.getInputStream(),1024));
                                    }
                                    catch (IOException e2)
                                    {
                                      e2.printStackTrace();
                                    }
     
                                    int method =_in.readInt();
     
                                    switch (method)
                                    {
                                      // notification
                                      case 1:
                                            doNotification(_in);
                                            break;
                                    }
                            }
                    }
                    catch (Throwable e)
                    {
                            e.printStackTrace();
                            Log.e(getClass().getSimpleName(), "Error in Listener",e);
                    }
     
                    try
                    {
                      ss.close();
                    }
                    catch (IOException e)
                    {
                      Log.e(getClass().getSimpleName(), "keep it simple");
                    }
            }
     
            },"Server thread");
          serverThread.start();
     
      }
     
      private void doNotification(DataInputStream in) throws IOException {
        String id = in.readUTF();
        displayNotification(id);
      }
     
      @Override
      public void onDestroy() {
              stopped=true;
              try {
                      ss.close();
              } catch (IOException e) {}
              serverThread.interrupt();
              try {
                      serverThread.join();
              } catch (InterruptedException e) {}
      }
     
      public void displayNotification(String notificationString)
      {
        int icon = R.drawable.mp_warning_32x32_n;
        CharSequence tickerText = notificationString;
        long when = System.currentTimeMillis();
        Context context = getApplicationContext();
        CharSequence contentTitle = notificationString;
        CharSequence contentText = "Hello World!";
     
        Intent notificationIntent = new Intent(this, MainApp.class);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
     
        Notification notification = new Notification(icon, tickerText, when);
        notification.vibrate = new long[] {0,100,200,300};
     
        notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
     
        String ns = Context.NOTIFICATION_SERVICE;
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
     
        mNotificationManager.notify(1, notification);
      }
     
    }

    This service listens on port 12345. When it receives some data it checks if the first “int” sent is “1”. If so, it then creates a new notification using the next piece of data (a string) that is received over the socket.

  8. Modify the MainApp Java class to start the service when the onCreate() method is called:
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		try
    		{
    			Intent srv = new Intent(this, TestService.class);
    			startService(srv);
    		}
    		catch (Exception e)
    		{
    			// service could not be started
    		}
     
    		super.onCreate(savedInstanceState);
    	}

    That is all you need to do in the Android application.

  9. Next, create a Flex application that will connect to the socket and send the right data. Here is some sample code for my Notifier.mxml class, which I used to test the Android service:
    <?xml version="1.0" encoding="utf-8"?>
    <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
                           xmlns:s="library://ns.adobe.com/flex/spark">
     
      <fx:Style>
        @namespace s "library://ns.adobe.com/flex/spark";
     
        global {
          fontSize: 32;      
        }
      </fx:Style>
     
      <s:layout>
        <s:VerticalLayout horizontalAlign="center" paddingTop="20"/>
      </s:layout>
     
      <s:TextInput id="t" text="test test"/>
     
      <s:Button label="create notification">
        <s:click>
          <![CDATA[
            var s:Socket = new Socket();
            s.connect("localhost", 12345);
            s.addEventListener(Event.CONNECT, function(event:Event):void {
              trace('connected!');
              (event.currentTarget as Socket).writeInt(1);
              (event.currentTarget as Socket).writeUTF(t.text);
              (event.currentTarget as Socket).flush();
              (event.currentTarget as Socket).close();
            });
            s.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent):void {
              trace('error! ' + event.errorID);
            });
            s.addEventListener(ProgressEvent.SOCKET_DATA, function(event:ProgressEvent):void {
              trace('progress ');
            });
          ]]>
        </s:click>
      </s:Button>
     
    </s:Application>

    As you can see there is just a TextInput control that allows the user to enter some text. Then when the user clicks the Button the AIR for Android application connects to a local socket on port 12345, writes an int with the value of 1, writes the string that the user typed into the TextInput control, and finally flushes and closes the connection. This causes the notification to be displayed.

  10. Now simply compile the Flex app and overwrite the assets/app.swf file with the new Flex application. Check out a video demonstration of this code.

Widgets

Widgets in Android are the mini apps that can be displayed on the home screen of the device. There is a fairly limited amount of things that can be displayed in Widgets. So unfortunately Widgets can’t be built with AIR for Android. However a custom application Widget can be packaged with an AIR for Android application. To add a Widget to an AIR for Android application you can use the default AppEntry class instead of wrapping it with another class (MainApp in my example). (It doesn’t, however, do any harm to keep the MainApp class there.) To add a Widget simply add its definition to the AndroidManifest.xml file, create the Widget with Java, and create a corresponding layout resource.

  1. First define the Widget in the application section of the AndroidManifest.xml file:
    <receiver android:name=".AndroidWidget" android:label="app">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data android:name="android.appwidget.provider" android:resource="@xml/airandroidwidget" />
    </receiver>
  2. You need an XML resource that provides metadata about the widget. Simply create a new file named airandroidwidget.xml in a new res/xml directory with the following contents:
    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minWidth="294dp"
        android:minHeight="72dp"
        android:updatePeriodMillis="86400000"
        android:initialLayout="@layout/main">
    </appwidget-provider>

    This tells the widget to use the main layout resource as the initial layout for the widget.

  3. Create a res/layout/main.xml file that contains a simple text display:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/widget"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#ffffffff"
        >
    <TextView  
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="hello"
        />
    </LinearLayout>

    Next, you’ll need to create the AppWidgetProvider class specified in the AndroidManifest.xml file.

  4. Create a new Java class named AndroidWidget with the following contents:
    package com.jamesward;
     
    import android.app.PendingIntent;
    import android.appwidget.AppWidgetManager;
    import android.appwidget.AppWidgetProvider;
    import android.content.Context;
    import android.content.Intent;
    import android.widget.RemoteViews;
    import com.jamesward.MainApp;
     
    public class AndroidWidget extends AppWidgetProvider
    {
     
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
        {
            final int N = appWidgetIds.length;
     
            // Perform this loop procedure for each App Widget that belongs to this provider
            for (int i=0; i<N; i++)
            {
                int appWidgetId = appWidgetIds[i];
                Intent intent = new Intent(context, MainApp.class);
                intent.setAction(Intent.ACTION_MAIN);
                PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
                RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
                views.setOnClickPendingIntent(R.id.widget, pendingIntent);
                appWidgetManager.updateAppWidget(appWidgetId, views);
            }
        }
    }

    This class will display the Widget when necessary and register a click handler that will open the MainApp application when the user taps on the Widget.

  5. Run the application to verify that it works.
  6. Now you can add the widget to the home screen by holding down on the home screen and following the Widget wizard.
  7. Verify that tapping the widget launches the AIR application.

Application Licensing

Android provides APIs to help you enforce licensing policies for non-free apps in the Android Market. You might want to go read up on Android Licensing before you give this one a try.

To add Application Licensing to you AIR for Android application you first need to follow the steps outlined in the Android documentation. The broad steps are as follows:

  1. Set up an Android Market publisher account
  2. Install the Market Licensing Package in the Android SDK
  3. Create a new LVL Android Library Project in Eclipse
  4. Add a Library reference in the Android project to the LVL Android Library
  5. Add the CHECK_LICENSE permission to your Android project’s manifest file:
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />

After completing these set up steps, you are ready to update the MainApp Java class to handle validating the license:

package com.jamesward;
 
import com.android.vending.licensing.AESObfuscator;
import com.android.vending.licensing.LicenseChecker;
import com.android.vending.licensing.LicenseCheckerCallback;
import com.android.vending.licensing.ServerManagedPolicy;
 
import air.Foo.AppEntry;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Secure;
 
public class MainApp extends AppEntry {
 
    private static final String BASE64_PUBLIC_KEY = "REPLACE WITH KEY FROM ANDROID MARKET PROFILE";
 
    // Generate your own 20 random bytes, and put them here.
    private static final byte[] SALT = new byte[] {
        -45, 12, 72, -31, -8, -122, 98, -24, 86, 47, -65, -47, 33, -99, -55, -64, -114, 39, -71, 47
    };
 
    private LicenseCheckerCallback mLicenseCheckerCallback;
    private LicenseChecker mChecker;
    private Handler mHandler;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler();
        String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
        mLicenseCheckerCallback = new MyLicenseCheckerCallback();
        mChecker = new LicenseChecker(
            this, new ServerManagedPolicy(this,
                new AESObfuscator(SALT, getPackageName(), deviceId)),
            BASE64_PUBLIC_KEY);
        mChecker.checkAccess(mLicenseCheckerCallback);
    }
 
    private void displayFault() {
        mHandler.post(new Runnable() {
            public void run() {
                // Cover the screen with a messaging indicating there was a licensing problem
                setContentView(R.layout.main);
            }
        });
    }
 
    private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
        public void allow() {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // Should allow user access.
        }
 
        public void dontAllow() {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            displayFault();
        }
 
        public void applicationError(ApplicationErrorCode errorCode) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
        }
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mChecker.onDestroy();
    }
}

Also add the following to a new res/layout/main.xml file in order to display an error when the license is denied:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
 
  <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/license_problem"
    />
 
</LinearLayout>

The text to display uses a string resource named “license_problem”, which must be added to the res/values/strings.xml file:

<string name="license_problem">THERE WAS A PROBLEM LICENSING YOUR APPLICATION!</string>

When the application runs it will check for a valid license. If the license comes back as valid then the AIR application will start and run as usual. However, if there is an invalid license then the application will set the ContentView to the R.layout.main resource, which displays the error message defined in the “license_problem” resource. To simulate different responses you can change the “Test Response” in your Android Market profile.

The Gory Details
I’ve wrapped up a generated AppEntry class and its resources to make the process of extending AIR for Android fairly easy. If you are interested in seeing how that is done, I’ve posted all of the source code on github.

Here is an overview of the procedure:

  1. Use the AIR SDK to create an AIR for Android APK file.
  2. Use the dex2jar utility to convert the AppEntry dex classes into a JAR file.
  3. Pull the resource classes out of the JAR file so that they don’t conflict with the new resources.
  4. Use apktool to extract the original resources out of the AIR for Android APK file.
  5. Create a single ZIP file containing the airbootstap.jar file, resources, AndroidManifest.xml file, and assets.

Now you can simply copy and paste those dependencies into your Android project.

Conclusion
Hopefully this article has helped you to better understand how you can extend AIR for Android applications with Android APIs. There are still a number of areas where this method can be improved. For instance, I am currently working with the Merapi Project developers to get Merapi working with my method of extending AIR for Android. That will provide a better bridging technique for communicating between the AIR application and Android APIs. So stay tuned for more information about that. And let me know if you have any questions!

This entry was posted in Adobe AIR, Android, Flex, Mobile. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
  • Pingback: Connecting AIR to Puredata | Studio 4 Development()

  • Gord

    I am trying to run this on the emulator. When I run it a popup ask me if I want to install Adobe Air. Then when I click “install” the popup disappears, but the Hello World is not displayed. If I then click the app icon on the desktop it again asks to install Air, which would seem to indicate that Air was not installed. Does this just not work on the emulator or have I broken something?

    Thanks

    • Gord

      Of course the answer is obvious – you have to manually install Air on the Android emulator. Just search for “How to install Air on Android Emulator” if you are having this problem.

  • Chetan

    Thanks for a great post.

    It works just fine and we can begin leveraging the services that aren’t currently supported by AIR natively.

    On the technical side, it’s great. However in practical terms there are some issues I am facing with the publishing side of things.

    When building a “regular” Adobe AIR app and uploading to the Android Market the app is assigned an id such as “air.com.yourcompany.AppName”. the “air.” Is automatically prefixed and the App is only visible for download to devices that actually support the AIR runtime.

    However when packaging the Application using native Android SDK, this no longer appears to happen. The App gets an id like “com.yourcompany.AppName”.

    This wouldn’t matter but now the App is visible to users on devices that don’t support the AIR runtime and they are able to download it. When installing however it says that the App isn’t supported on their device, leaving the user disappointed.

    Any insight into this?

  • Tanweer

    Hi James,

    Do you have any idea how to make use of iOS resources in a similar way.

    Thanks

    Tanweer

  • saurabh

    HI, Great article.I am able to run this hello world application.I have one question correct me if I am wrong.Here the android framework is launching the code written in flex from the file –> app.swf right?? Which has the code of hello World inside it.Now suppose if I want to run another .swf file being created from the Flex what changes and and which files I have to change .I did try it by replacing the app.swf with the myflexapp.swf and app with the myflexapp in the application.xml file but it doesn’t work.Any way to make this run.Also I need to pass the variables/data to this flex code and finally run it and then come back to the android code(activity).Plz assist.
    Rgds,
    Saurabh

  • chivorotkiv

    Hi. I’m trying to run this application.

    It says me: “This application requires Adobe AIR. Install Adobe AIR now?” (http://screencast.com/t/7xrxWQx5E) Is it normal?

    From your article I thought that you find a way to pack AIR into .apk file and run it without any additional installations.

    • saurabh

      Hi chivorotkiv,
      I too faced the same problem.All you need to do is google “installing AIR for android emulator – froyo or gingerbread” , you will definitely get the way to install the AIR for android apk on your emulator using the adb shell.Hope it will help you.
      Cheers !
      Saurabh

      • chivorotkiv

        Hi saurabh.

        I see that. But I think, not every user understand what is AIR, and some users will click “Cancel” when application ask them to install AIR. So, we loose those users.

        Do you know any way to pack AIR right into .apk package? It would be very interesting.

        • saurabh

          Hi chivorotkiv,
          Hmmmm I see, ok I will surely let you know if i come across such thing.My mail id is rawatsaurabh@yahoo.co.in .PM me in case you feel.
          Rgds,
          Saurabh

  • Flamy

    Hey nice work :) btw is there a way to send data from android to swf. wat u r doin now is jus listening a port and swf sends data. I need the vice versa too is it possible with the same socket or do i have to create one more connection. The reason i am asking this is coz the swf is not listening to any port and Native code is unaware of swf’s port. So shd i create a new connection,even if i do so will i be able to send data to air?!

    • saurabh

      HI Flamy,
      I too want to do soemthing similar in which we can pass the data to the .swf at runtime and .swf can be build also accordingly at the run time in eclipse.Are you talking the same thing.Do share your views.PM me at rawatsaurabh@yahoo.co.in .I am trying to integrate the .swf(flex code) inside the eclipse and want the data to be passed at the run time to the flex code and embed the .swf file inside the android apk.
      Rgds,
      Saurabh

  • Pingback: Using AIR and Java together « Dobos Bence()

  • http://www.rybram.com Rybram.com

    I built an example project using a modified version of the native extension adobe makes available. Its already compiled all you have to do is put in your public key and follow the tutorial I have on my site.

    http://www.rybram.com/index.php/blog/61-air-30-android-licensing-tutorial-project

    Might be worth a look for anyone wanting to use the licensing extension.

  • http://bradwallacedesign.com Brad

    love this post!

    this is still the ONLY working method to get notifications in an air app for android! its been a while now since adobe created its notification native extension but i can’t get it to work. and as far as i can tell, no one else can either. licensing and vibration seem to work (as well as some others), but not the notifications!

    could you make this a slightly more full featured version and allow android to send messages to the app when a user touches the notification? this would be so helpful to any of us who have an app that uses notifications and simply can’t wait till the end of our lives for adobe to fix their native extension.

    thanks so much!

  • Mónica

    is the method onCreate(Bundle arg0) of AppEntry passing the bundle atributtes to the swf?? how could i have this arguments in the swf (ViewNavigatorApplication)?
    Thanks in advance!

  • NataRaj

    In java Native Extension project , can we use 3rd party jars?

    • http://www.jamesward.com James Ward

      You can use anything that works in a native Android project.

  • frgo

    Hi James, I have many flash files that I would like to expose in an Android App. However the content is too big to  wrap within the .apk. 

    I would like to place all .swf files on the sdcard and load them dinamically as the user selects an item from a list. 

    I managed to do that rendering the .swf on an Android  WebView (which actually uses FlashPlugin to render), however the behavior is not the same (some of the existing flash files actually use native calls only available in Air). 

    Do you see a way to render a .swf file from the sdcard dynamically on the Air engine? I mean, can I programmatically do that? 

    Thanks in advance

  • http://www.facebook.com/derhong Derhong Yang

    Hi James,

    I use the method you describe here to launch app.swf from Java. However, the swf cannot receive any keyboard event. Is this a limitation of AppEntry? or something else?

    Related logs are as below:

    W/KeyCharacterMap( 1953): Can’t open keycharmap fileW/KeyCharacterMap( 1953): Error loading keycharmap file ‘/system/usr/keychars/vkeyboard.kcm.bin’. hw.keyboards.65538.devname=’vkeyboard’W/KeyCharacterMap( 1953): Using default keymap: /system/usr/keychars/qwerty.kcm.bin

    Thanks in advance.

    – derhong 

  • Bigmandoo

    awesome works!
    but i found the animation performance slower than FB generated apk. it seems like AppEntry class does not enables gpu acceleration. is there any solution?

  • Kamil

    Hey I just tried this and your example works great, however when I add my own swf it crashes instantly. I built my flex app in flash builder 4.6 air version 3.7, when I launch it doesn’t even give me the “test test” response in console. where should my resources for the swf file be placed, and could that be the problem? please help, Thanks.

    • Kamil

      I meant air version 3.1

  • Andrew

    Hi James et al

    I still like this technique however cannot get it to load properly with AIR 3.0 SWFs.
    Have you or anyone else been able to? What is the trick?

    I believe, but not certain, that it worked with 2.6 & 2.7.
    Definitely with 2.5.

  • Andrew

    Well, minutes after my last post I tried Exporting the App to an APK and then installing that on my device. Which worked.
    Previously, I was only running it from Eclipse or Debugging it. It would fire up but the SWF would never appear.

    After doing the export of the APK and installing it, I can run and debug from Eclipse.

  • Pingback: Using Adobe’s Notification Air Native Extension with Flash Professional | BradWallaceDesign.com blog()

  • Abhisek Paul

    any idea how to debug the jar file while developing the native extension…

  • Dilawar

    Thnx…

  • Andrew

    Hi James. Can you or anyone confirm that this still works?
    I have encountered numerous errors since I last updated the SDK.
    The first of which saying: unable to instantiate activity ComponentInfo{air.app/air.app.AppEntry}
    Previously installed apps using this technique still work but newly created ones bail when first trying to run it.
    Anyone..?

  • Andrew

    In response to myself, here is what I had to do:
    Right click the project and go to  properties -> Java Build Path -> Order and Export  and check the airbootstrap.jar

    Better explained here: http://android.foxykeep.com/dev/how-to-fix-the-classdefnotfounderror-with-adt-17
    and here: http://tools.android.com/recent/dealingwithdependenciesinandroidprojects

    • Sindhushree

      Hi Andrew, even am also facing same problem but I follows your suggession still didn’t work for me.
      And am referring this because I need get contacts in android device so.. do you know the solution?

  • Mohamad Salloum

    Awesome ..

  • Jorge

    Hello James, do u know a way to do the same with iOS ?

  • maple

    Can we use this concept in the following scenario:

    1. Graphic designers create colorful, animated user interfaces in Adobe.

    2. Eclipse developers create behind-the-scenes code for accessing databases, logging, network connections, etc
    3. Connection between the two is the socket connection you outline above.
    Thanks,

  • Fenil

    Hi. very nyc stuff. i am trying to embed my flex apk into android native apk. i am getting sucess into it but at the time of installation of that combine apk the application is asking to install application for both but i want do it into one step installation for apk which is combination of two hybrid apk.

  • jozest

    Sory i want to ask how we can get phone property from android device such as imei. thx

  • http://sumeetbasak.wordpress.com Sumeet Basak

    This is a very old post and i think this might be a way out to add INSTALL_REFERRER to android apps. While i try my hands on this, i realise this post is a 3 year old solution and may or may not work with the latest build of AIR 3.9. I think this approach will be fruitful to add hooks to the start of an app and read information that the google play app store passes as a broadcast intent. Is this approach totally not supported by adobe anymore?



  • View James Ward's profile on LinkedIn