I want to create the update mechanism of my Adobe Air-based Android app. In my special case with a private, very small app audience I use a direct distribution (without Google Play, via phisical installing on the users device) of the my application apk-file. But also I need to be able to install updates to it automatically (Let's say it will be a button 'Check updates' in my app UI). Can anybody help me with this task? How can I create this?
Now I have the idea:
I try to download the file with my app (using loaders and write the file using the File api) to the user storage... Then I want to use any Air Native Extension to launch that file. Is there any already created ANE to do that?
In my case for create auto update mechanism, I got local version number from app.xml and latest number from server.
Here is my settings.
app.xml
...
<name>mysomeapp</name>
<!-- A string value of the format <0-999>.<0-999>.<0-999> that represents application version which can be used to check for application upgrade.
Values can also be 1-part or 2-part. It is not necessary to have a 3-part value.
An updated version of application must have a versionNumber value higher than the previous version. Required for namespace >= 2.5 . -->
<versionNumber>1.0.1</versionNumber>
...
actionscript
private var appVersion: String;
public function init():void
{
// Get local versionnumber from app.xml
var appDescriptor:XML = NativeApplication.nativeApplication.applicationDescriptor;
var ns:Namespace = appDescriptor.namespace();
appVersion = appDescriptor.ns::versionNumber; // In this case, it returns "1.0.1".
// Get latest versionnumber from server
var httpservice: HTTPService = new HTTPService();
httpservice.url = "http://myserver/foobar/version";
httpservice.method = URLRequestMethod.GET;
httpservice.addEventListener(ResultEvent.RESULT, checkVersionResult);
httpservice.send();
}
public function checkVersionResult(e: ResultEvent): void
{
// Compare latest number with local number.
if (e.result != appVersion)
{
// If the local version is not latest, download latest app from server.
navigateToURL(new URLRequest("http://myserver/foobar/androidAppDownload"));
}
}
We are using Cordova along with AngularJS for iOS and Android applications.
One big disadvantage of iOS are the long review times from Apple. In Google's Playstore, your app is available nearly immediately, or within a few hours. But Apple takes ages to review your app, even when it's only a small change.
So I was thinking, if there is a way to support some kind of live update.
That means, I could provide a ZIP file or something else with a new codebase, my app checks for updates and then installs the new files.
I've read something from appmobi, but are there any open source solutions?
cordova-app-loader is an easy to use plugin to update app files via 3 simple steps:
check() for a new manifest
download() files
update() your app!
It supports android and iOS
I don't know of any ready made solutions for that, but it should be easy enough to program something like this on your own.
Here are some points to get you started and to consider:
If you want to distribute updates via zip, you need a nativ plugin which handles the extraction
You might not be able to override files in the default location of your app (depending on OS). So, all files you want to update in the future have to sit in a folder your app has read/write access to (iOS: e.g. Library or Documents folder)
Now you simply need to download the zip-package, unpack the zip to your chosen directory, and restart/reload your app.
you will not be able to update native plugins!
Apple probably doesn't like that, since you are able to change the whole application without passing
their review process
I'm doing this inside my cordova app and haven't had any issues with ios app store review.
I'm using Jquery's ajax function to download both a javascript and a css file from a server that I can change without an app store approval and then I can inject those scripts once they downloaded on app startup.
I tried using the cordova File api and I'd then save the file locally, but offline support ins't the important to me at the moment and Jquery's ajax is much simpler.
Here is the jquery code I use. I have a bundle id that I use to detect if a new javascript file is available, otherwise jquery's ajax caches the previous requests to speed up download time.
This solution lets you have a subset of your code be dynamic. I still have a base set of code that is bundled with the app, along with native plugin js and native code which would need to go through the app store. But this atleast lets me push bug fixes without going through the app store.
Otherwise, I'd look at a solution like this: http://docs.build.phonegap.com/en_US/tools_hydration.md.html
function insertScript(version) {
var scriptUrl = "";
try {
// get javascript file...
scriptUrl = mobileWebServiceUrl + "/DynamicContent/Bundles/Scripts/dynamic";
scriptUrl += "_" + bundleVersion.replace(/\./g, "_") + ".js?v=" + version;
console.log("downloading script: " + scriptUrl);
// Allow user to set any option except for dataType, cache, and url
options = {
dataType: "script",
cache: true,
url: scriptUrl
};
// Use $.ajax() since it is more flexible than $.getScript
// Return the jqXHR object so we can chain callbacks
return $.ajax(options).success(function(response) {
console.log("insertScript success");
dynamicContentScriptLoaded = true;
});
} catch (e) {
//console.error(e);
ReportError("problem downloading javscript: " + scriptUrl);
}
}
function insertCSS(version) {
try {
// get css file...
var cssUrl = mobileWebServiceUrl + "/DynamicContent/Bundles/Css/dynamic";
cssUrl += "_" + bundleVersion.replace(/\./g, "_") + ".css?v=" + version;
console.log("downloading dynamic css: " + cssUrl);
$.ajax(cssUrl)
.success(function (response) {
console.log("successfully downloaded dynamic css");
var script = document.createElement("style");
script.type = "text/css";
script.innerHTML = response;
$('head link').each(function () {
if ($(this).attr('href').search('MobileFrame') > -1) {
$("#MobileFrameCSS").before(script);
}
});
dynamicContentCssLoaded = true;
// TODO: implement caching at a later date
//if (isPhoneGap())
// saveFile("DynamicStyles", response);
});
} catch (e) {
ReportError("problem downloading css");
}
}
Well, Adobe offers exactly that service in their Phonegap Build service. It's called Hydration.
The example shows using it with Android and iOS platforms, so I guess they made it compatible with the iOS Dev Program License Agreement.
If you are using Cordova, you probably will have to switch to the Phonegap CLI if you want to use their build cloud services, which is basically the same as Cordova's with some extra commands to upload to their cloud, etc.
I think there are some plugin like Splashscreen wich also have some minor changes (using <gap>for params into config.xml instead of <preference>). Again, if Hydration solves the problem for you, the changes are minor and you get a really nice feature.
I think the best choice would be to not try to do this with Phonegap, but rather identify your dynamic parts and implement these in Javascript.
Yes, I mean you should indeed use Javascript yourself without Phonegap, for example via JavaScriptBridge:
https://github.com/kishikawakatsumi/JavaScriptBridge
It may require more work initially to redesign your app into a "static" part (your PhoneGap app) and dynamic part (dynamic created views via JavascriptBirdge), and interacte seemlessly between them. But in my opinion, that will be ultimately the best software design.
However, also make sure you still meet Apples AppStore requirements.
The Meteor framework provides exactly this functionality when combined with PhoneGap. It's even sanctioned by Apple in the latest Developer Agreement. Here are some technical details and then some about Apple's view on it.
I think there is no such solution is available, but you can do it by programmatic way.you can update your cardova app by fetching files from server and updating it.
Check out CodePush from Microsoft. Works with Cordova and React Native.
Appears to be very similar to the "live update" feature from Ionic Cloud.
If you migrate to capacitor, the successor of Cordova there open source solution now.
Capacitor-updater, is the only alternative to ionic AppFlow.
The updater allows you to manage update by yourself, store your zip update where you want and use the download method.
How to start
npm install #capgo/capacitor-updater
npx cap sync
Then in your main JS, this is required to let the updater know the update is valid
import { CapacitorUpdater } from '#capgo/capacitor-updater'
CapacitorUpdater.notifyAppReady()
And lately after checking yourself the current version need update:
const version = await CapacitorUpdater.download({
url: 'https://github.com/Cap-go/demo-app/releases/download/0.0.4/dist.zip',
})
await CapacitorUpdater.set(version); // sets the new version, and reloads the app
After many request of people didn't want to do that themselves, I started Capgo a business to manage all the update process.
All is open source and can be replicate on your own as well.
Doing things for Capacitor is now my main activity, I produce open-source plugin as my main channel of Marketing, I'm solo founder and bootstrapped.
Hope my tool will help you !
I want users to be able to share a url link on google plus from inside my as3 app using navigateToUrl, but I want the native google plus app to open up, not go through mobile web browser. I am using the as3 function below for iOS which works perfectly fine:
function shareOnGooglePlus(event:Event):void {
var request:URLRequest = new URLRequest("gplus://plus.google.com/share?url=" + URL_LINK);
navigateToURL(request, "_blank")
}
This function just isn't doing anything on android and I don't know why. Does anyone have any ideas on this?
Thanks in advance
I would like to code a function which will let me know if another specific app of my company is installed on the current device. I guess that in order to do so I need to retrieve a list of the installed apps like here (Answer for native coders):
How to get a list of installed android applications...
Is there a straight forward way to get the list with action script?
Bottom line, I just need to know about my own apps. Maybe a way to get a list of air apps installed?
Thank you
The direct answer no, but you can use one of the following:
1- Write a file in specific location indicates that the app from your company installed, and check this file from your app, to do this
- On the first time run of the application write a file in the desktopDirectory:
var myAppTextFile:File = File.desktopDirectory.resolvePath("MyAppVersion.txt");
var fs:FileStream = new FileStream();
fs.open(myAppTextFile, FileMode.WRITE);
fs.writeUTF("MyApp V0.0 installed");
fs.close();`
Each time you run the New Application check this file:
var myAppTextFile:File = File.desktopDirectory.resolvePath("MyAppVersion.txt");
var fs:FileStream = new FileStream();
fs.open(myAppTextFile, FileMode.READ);
var str:String = fs.readUTF();
fs.close();`
Note: Works for Android not for iOS.
2- Use Native extension to do this.
The following code is a snippet of a much larger project I'm working on. The problem is when I test the app in Flash CS6 urlLoader reads 25Notes.txt perfectly but when I try debugging the app (via Flash's built in "Debug>Debug Movie>On Device via USB") on my Android device (HTC Incredible) urlLoader throws a stream error #2032.
I'm deploying the app with Captive Runtime from AIR 3.2 using Flash CS6 and my project is organized with no extraneous files or folders.
Please let me know if you have any insight or suggestions and if you need more of an explanation or code.
I'm also not sure if Captive Runtime in Flash CS6 packages all of the local files in the root project folder or just the ones used in the app or none at all. Any information about this would be greatly appreciated as well.
var FILE_LIST_PATH:String = "/25Notes/25Notes.txt";
var mp3List:Array = new Array();
LoadFile();
function LoadFile()
{
var urlRequest:URLRequest = new URLRequest(FILE_LIST_PATH);
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, processTextFile);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
urlLoader.load(urlRequest);
}
function onIOError(e:IOErrorEvent){
trace("can't load file list: " + e);
}
function processTextFile(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
var textFile:String = loader.data;
mp3List = textFile.split("\n");
for (i=0;i<mp3List.length;i++){
trace(mp3List[i],i);
}
}
Here's what the IOError trace returns:
can't load file list: [IOErrorEvent type="ioError" bubbles=false cancelable=false eventPhase=2 text="Error #2032: Stream Error. URL: app:/25Notes/25Notes.txt" errorID=2032]
I'm totally stumped. Please help!!
Is the file being saved by the app at runtime and you are trying to retrieve it? If not, that URL is incorrect. The app:/ directory is the applicationStorageDirectory on the device, not a location within the app itself. You'll need to embed your txt file as a Class and access it that way
[Embed(source="assets/ui/images/phone/menu-myvideos-active-160.png")]
var MyVideosActive160:Class;
That is something from a project I am actually working on finishing right now. That embeds an image and allows me to access it afterward. To access it, I simply do
var bmp:Bitmap = new MyVideosActive160();
If you don't embed, I don't believe you can actually access the file. Every file that needs to be included within the app must be embedded.
If you are saving the file at run time at some point, you'll want to do it this way
var path:String = new File(new File.applicationStorageDirectory.resolvePath("/25Notes/25Notes.txt").nativePath).url;
That will output a file:/// URL, rather than a url using app:/. I am using this method to play downloaded videos in my app. It's a crappy workaround that Adobe should have built into SDK, but that's what we have to deal with.