I am creating an ANE for Urban Airship, a service for sending push notifications (among other things). So far the integration has worked great but only when the app is open. When the app is exited, receiving a new push notification results in the app crashing with:
11-29 01:19:32.448 22340-22340/air.com.example.app E/Urban Airship Autopilot: Unable to takeOff automatically
11-29 01:19:32.496 22340-22440/air.com.example.app E/AndroidRuntime: FATAL EXCEPTION: IntentService[PushService]
Process: air.com.example.app, PID: 22340
java.lang.IllegalStateException: Take off must be called before shared()
at com.urbanairship.UAirship.shared(UAirship.java:147)
at com.urbanairship.BaseIntentService.onHandleIntent(BaseIntentService.java:94)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java)
at android.os.Handler.dispatchMessage(Handler.java)
at android.os.Looper.loop(Looper.java)
at android.os.HandlerThread.run(HandlerThread.java)
Through a lot of digging I believe the issue to be that I am calling UAirship.takeOff() from within TakeoffFunction (an FREFunction within my ANE) instead of calling it from the Application's main onCreate method (as is the issue seen in: UrbanAirship NPE)
Here is my TakeoffFunction:
public class TakeoffFunction implements FREFunction
{
@Override
public FREObject call(FREContext context, FREObject[] freObjects)
{
Log.d("TakeoffFunction", "Attempting Urban Airship TAKEOFF");
Application app = context.getActivity().getApplication();
Log.d("TakeoffFunction", "app found: " + app);
AirshipConfigOptions options = new AirshipConfigOptions();
options.developmentAppKey = "xxx";
options.developmentAppSecret = "xxx";
options.inProduction = false;
options.gcmSender = "000";
Log.d("TakeoffFunction", "Prepare to Launch...");
UAirship.takeOff(app, options, new UAirship.OnReadyCallback()
{
@Override
public void onAirshipReady(UAirship uAirship)
{
Log.d("TakeoffFunction", "Urban Airship is ready after takeoff");
uAirship.getPushManager().setUserNotificationsEnabled(true);
Log.d("TakeoffFunction", "User notifications have been enabled");
}
});
return null;
}
}
Thus, it would seem I need to somehow call UAirship.takeOff() from the main application's onCreate method. However, this is proving to be a challenge as I know that for Adobe AIR, there is an AppEntry class which functions as the main Application class, but this class is, as far as I know, barred from modification for developers. I found this tutorial: http://blogs.adobe.com/digitalmedia/2011/05/extending-air-for-android/ from back in 2011 before native extensions were officially supported. In there I see that they're able to override and extend the onCreate() method, but I don't know how I would go about doing the same thing with this native extension.
I would like to know if it is possible to extend the AppEntry's onCreate method or point AIR to a different AppEntry class altogether, overwriting the original.
Its common to see libs wanting to have the app initialize its module in the application's onCreate as its called before all services, receivers, or activities are created. Basically the only real entry point for all apps.
For frameworks like Adobe where its hard to call takeoff in the applicaiton file, we have added another way of calling takeoff using Autopilot.
Example:
public class AirAutopilot extends Autopilot {
@Override
public AirshipConfigOptions createAirshipConfigOptions(Context context) {
AirshipConfigOptions options = new AirshipConfigOptions();
options.developmentAppKey = "xxx";
options.developmentAppSecret = "xxx";
options.inProduction = false;
options.gcmSender = "000";
return options;
}
@Override
public void onAirshipReady(UAirship airship) {
Log.d("TakeoffFunction", "Urban Airship is ready after takeoff");
airship.getPushManager().setUserNotificationsEnabled(true);
Log.d("TakeoffFunction", "User notifications have been enabled");
}
}
Then add the autopilot class to the manifest in the application block (not sure about the Adobe Air way of doing this):
<meta-data android:name="com.urbanairship.autopilot" android:value="com.example.AirAutopilot" />
Then in your plugin, make sure a call to autopilot is made before accessing the UAirship instance:
Autopilot.automaticTakeOff(context.getActivity().getApplication());