diff --git a/.gitignore b/.gitignore index d40db17f..8001b038 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ default.properties -bin gen assets/www/phonegap.js local.properties diff --git a/bin/droidgap b/bin/droidgap new file mode 100755 index 00000000..81fa48da --- /dev/null +++ b/bin/droidgap @@ -0,0 +1,150 @@ +#!/usr/bin/env ruby +ROOT = File.expand_path(File.dirname(__FILE__).gsub('bin','')) +require 'fileutils' +require File.join(ROOT, "lib", "generate.rb") +require File.join(ROOT, "lib", "package.rb") +require File.join(ROOT, "lib", "run.rb") +require File.join(ROOT, "lib", "update.rb") + +# ---------------------------------------------------------- # +# # +# command line interface # +# # +# ---------------------------------------------------------- # + +# droidgap gen [app name] +Generate.new(ARGV[1]) if ARGV.first == 'gen' + +# droidgap pkg [path to phonegap project] +Package.new(ARGV[1]) if ARGV.first == 'pkg' + +# droidgap run [optional directory] +Run.new(ARGV[1]) if ARGV.first == 'run' + +# droidgap update [params] +Update.new if ARGV.first == 'update' + +# droidgap log +if ARGV.first == 'log' + $stdout.sync = true + IO.popen('adb logcat') do |f| + until f.eof? + puts f.gets + end + end +end + + +puts "droidgap ship not implemented" if ARGV.first == 'ship' + + +if ARGV.first.nil? || ARGV.first == 'help' + help = <<-EOF + + DroidGap: PhoneGap/Android Dev Script + ------------------------------------- + + Useful utilities for devlopers building mobile apps using PhoneGap for Android. + + Usage: + + droidgap + + Commands: + + help ..... See this message. Type help to see specific help topics. + gen ...... Generate an example PhoneGap application to current directory. + pkg ...... Creates an Android compatible project from a www folder. Careful, this clobbers previous packaging. + run ...... Installs a valid PhoneGap Project to first device found. + log ...... Attach a logger that listens for console.log statements. + update ... Copy a fresh phonegap.jar and phonegap.js into a valid PhoneGap/Android project. + ship ..... Build and sign an APK suitable for submission to an Android Marketplace. + + Quickstart: + + $ droidgap gen example + $ cd example + $ droidgap run + + Now you can launch your app and optionally start a logger with: + + $ droidgap log + + EOF + + gen = <<-EOF + + DroidGap Generate + ----------------- + + Generate an example PhoneGap application to path supplied or current working directory if none is supplied. + + Usage: + + droidgap gen [path] + + EOF + + run = <<-EOF + + DroidGap Run + ------------ + + Launches PhoneGap project to first device found and attaches a logger that listens for console.log statements. + + Usage: + + droidgap run + + EOF + + ship = <<-EOF + + DroidGap Ship + ------------- + + Build and sign an APK suitable for submission to an Android Marketplace. + + Usage: + + droidgap ship + + EOF + + log = <<-EOF + + DroidGap Log + ------------- + + Launches LogCat + + Usage: + + droidgap log + + EOF + + pkg = <<-EOF + + DroidGap Package + ---------------- + + Creates an Android compatable project from a PhoneGap project. For example, if you have MyProject with index.html this comamdn will create MyProject-android. + + Usage: + + droidgap pkg + + EOF + + update = <<-EOF + + Update: Builds the JS and PhoneGap Android jar file and copies them to your project. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + path ............... The path to generate the Android application. + + EOF + + puts ARGV[1].nil? ? help : eval(ARGV[1]) +end diff --git a/droidgap b/droidgap deleted file mode 100755 index b2f45b9b..00000000 --- a/droidgap +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env ruby -require 'fileutils' - -# ./droidgap /Users/brianleroux/Code/android-sdk-mac MyApp com.westcoastlogic example /Users/brianleroux/Desktop/MyApp -class Build - attr_reader :android_sdk_path, :name, :pkg, :www, :path - - def initialize(*a) - @android_sdk_path, @name, @pkg, @www, @path = a - @android_dir = File.expand_path(File.dirname(__FILE__)) - @framework_dir = File.join(@android_dir, "framework") - end - - # runs the build script - def run - build_jar - create_android - include_www - generate_manifest - copy_libs - add_name_to_strings - write_java - puts "Complete!" - end - - # removes local.properties and recreates based on android_sdk_path - # then generates framework/phonegap.jar - def build_jar - puts "Building the JAR..." - %w(local.properties phonegap.js phonegap.jar).each do |f| - FileUtils.rm File.join(@framework_dir, f) if File.exists? File.join(@framework_dir, f) - end - open(File.join(@framework_dir, "local.properties"), 'w') do |f| - f.puts "sdk.dir=#{ @android_sdk_path }" - end - Dir.chdir(@framework_dir) - `ant jar` - Dir.chdir(@android_dir) - end - - # runs android create project - # TODO need to allow more flexible SDK targetting - # TODO validate Android SDK - # TODO fix 'android' shell call so that it works on Windows. Can't prepend android command with path to it. - def create_android - android_exec = File.join(@android_sdk_path, "tools", "android"); - target_id = 8 - puts "Creating Android project for target level #{ target_id }" - `android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }` - end - - def include_www - puts "Adding www folder to project..." - - FileUtils.mkdir_p File.join(@path, "assets", "www") - FileUtils.cp_r File.join(@www, "."), File.join(@path, "assets", "www") - end - - # creates an AndroidManifest.xml for the project - def generate_manifest - puts "Generating manifest..." - manifest = "" - open(File.join(@framework_dir, "AndroidManifest.xml"), 'r') do |old| - manifest = old.read - manifest.gsub! 'android:versionCode="5"', 'android:versionCode="1"' - manifest.gsub! 'package="com.phonegap"', "package=\"#{ @pkg }\"" - manifest.gsub! 'android:name=".StandAlone"', "android:name=\".#{ @name }\"" - manifest.gsub! 'android:minSdkVersion="5"', 'android:minSdkVersion="3"' - end - open(File.join(@path, "AndroidManifest.xml"), 'w') { |x| x.puts manifest } - end - - # copies stuff from framework into the project - # TODO need to allow for www import inc icon - def copy_libs - puts "Copying over libraries and assets and creating phonegap.js..." - - framework_res_dir = File.join(@framework_dir, "res") - app_res_dir = File.join(@path, "res") - - FileUtils.mkdir_p File.join(@path, "libs") - FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs") - - FileUtils.mkdir_p File.join(app_res_dir, "values") - FileUtils.cp File.join(framework_res_dir, "values","strings.xml"), File.join(app_res_dir, "values", "strings.xml") - - FileUtils.mkdir_p File.join(app_res_dir, "layout") - %w(main.xml).each do |f| - FileUtils.cp File.join(framework_res_dir, "layout", f), File.join(app_res_dir, "layout", f) - end - - %w(drawable-hdpi drawable-ldpi drawable-mdpi).each do |e| - FileUtils.mkdir_p File.join(app_res_dir, e) - FileUtils.cp File.join(framework_res_dir, "drawable", "icon.png"), File.join(app_res_dir, e, "icon.png") - end - - # concat JS and put into www folder. - js_dir = File.join(@framework_dir, "assets", "js") - - phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base')) - - Dir.new(js_dir).entries.each do |script| - next if script[0].chr == "." or script == "phonegap.js.base" - phonegapjs << IO.read(File.join(js_dir, script)) - phonegapjs << "\n\n" - end - - File.open(File.join(@path, "assets", "www", "phonegap.js"), 'w') {|f| f.write(phonegapjs) } - end - - # puts app name in strings - def add_name_to_strings - puts "Adding some application name to strings.xml..." - x = " - - #{ @name } - Snap - - " - open(File.join(@path, "res", "values", "strings.xml"), 'w') do |f| - f.puts x.gsub(' ','') - end - end - - # this is so fucking unholy yet oddly beautiful - # not sure if I should thank Ruby or apologize for this abusive use of string interpolation - def write_java - puts "Writing application Java code..." - j = " - package #{ @pkg }; - - import android.app.Activity; - import android.os.Bundle; - import com.phonegap.*; - - public class #{ @name } extends DroidGap - { - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - super.loadUrl(\"file:///android_asset/www/index.html\"); - } - } - " - - code_dir = File.join(@path, "src", @pkg.gsub('.', File::SEPARATOR)) - - FileUtils.mkdir_p(code_dir) - open(File.join(code_dir, "#{@name}.java"),'w') { |f| f.puts j.gsub(' ','') } - end - # -end - - -if ARGV.length == 5 - Build.new(*ARGV).run -else - puts <<-EOF - - DroidGap: PhoneGap/Android Project Generator - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Creates a fresh app for hybrid mobile web hacking. Delicious robot! - - Usage: - - ./droidgap - - Params: - - android_sdk_path ... The path to your Android SDK install. - name ............... The name of your application. - package_name ....... The name of your package (For example: com.nitobi.demo) - www ................ The path to your www folder. (Wherein your HTML, CSS and JS app is.) - path ............... The path to generate the Android application. - - EOF -end \ No newline at end of file diff --git a/framework/assets/www/phonegap.js b/framework/assets/www/phonegap.js new file mode 100644 index 00000000..cfc98fab --- /dev/null +++ b/framework/assets/www/phonegap.js @@ -0,0 +1,1638 @@ +if (typeof(DeviceInfo) != 'object') + DeviceInfo = {}; + +/** + * This represents the PhoneGap API itself, and provides a global namespace for accessing + * information about the state of PhoneGap. + * @class + */ +var PhoneGap = { + queue: { + ready: true, + commands: [], + timer: null + } +}; + + +/** + * Custom pub-sub channel that can have functions subscribed to it + */ +PhoneGap.Channel = function(type) +{ + this.type = type; + this.handlers = {}; + this.guid = 0; + this.fired = false; + this.enabled = true; +}; + +/** + * Subscribes the given function to the channel. Any time that + * Channel.fire is called so too will the function. + * Optionally specify an execution context for the function + * and a guid that can be used to stop subscribing to the channel. + * Returns the guid. + */ +PhoneGap.Channel.prototype.subscribe = function(f, c, g) { + // need a function to call + if (f == null) { return; } + + var func = f; + if (typeof c == "object" && f instanceof Function) { func = PhoneGap.close(c, f); } + + g = g || func.observer_guid || f.observer_guid || this.guid++; + func.observer_guid = g; + f.observer_guid = g; + this.handlers[g] = func; + return g; +}; + +/** + * Like subscribe but the function is only called once and then it + * auto-unsubscribes itself. + */ +PhoneGap.Channel.prototype.subscribeOnce = function(f, c) { + var g = null; + var _this = this; + var m = function() { + f.apply(c || null, arguments); + _this.unsubscribe(g); + } + if (this.fired) { + if (typeof c == "object" && f instanceof Function) { f = PhoneGap.close(c, f); } + f.apply(this, this.fireArgs); + } else { + g = this.subscribe(m); + } + return g; +}; + +/** + * Unsubscribes the function with the given guid from the channel. + */ +PhoneGap.Channel.prototype.unsubscribe = function(g) { + if (g instanceof Function) { g = g.observer_guid; } + this.handlers[g] = null; + delete this.handlers[g]; +}; + +/** + * Calls all functions subscribed to this channel. + */ +PhoneGap.Channel.prototype.fire = function(e) { + if (this.enabled) { + var fail = false; + for (var item in this.handlers) { + var handler = this.handlers[item]; + if (handler instanceof Function) { + var rv = (handler.apply(this, arguments)==false); + fail = fail || rv; + } + } + this.fired = true; + this.fireArgs = arguments; + return !fail; + } + return true; +}; + +/** + * Calls the provided function only after all of the channels specified + * have been fired. + */ +PhoneGap.Channel.join = function(h, c) { + var i = c.length; + var f = function() { + if (!(--i)) h(); + } + for (var j=0; j frequency + 10 sec + var timeout = Accel.getTimeout(); + if (timeout < (frequency + 10000)) { + Accel.setTimeout(frequency + 10000); // set to frequency + 10 sec + } + + var id = PhoneGap.createUUID(); + Accel.start(); + + // Start watch timer + navigator.accelerometer.timers[id] = setInterval(function() { + var status = Accel.getStatus(); + + // If accelerometer is running + if (status == Accelerometer.RUNNING) { + try { + var accel = new Acceleration(Accel.getX(), Accel.getY(), Accel.getZ()); + successCallback(accel); + } catch (e) { + console.log("Accelerometer Error in successCallback: " + e); + } + } + + // If accelerometer had error + else if (status != Accelerometer.STARTING) { + console.log("Accelerometer Error: "+ Accelerometer.ERROR_MSG[status]); + try { + navigator.accelerometer.clearWatch(id); + if (errorCallback) { + errorCallback(status); + } + } catch (e) { + console.log("Accelerometer Error in errorCallback: " + e); + } + } + }, (frequency ? frequency : 1)); + + return id; +} + +/** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ +Accelerometer.prototype.clearWatch = function(id) { + + // Stop javascript timer & remove from timer list + if (id && navigator.accelerometer.timers[id]) { + clearInterval(navigator.accelerometer.timers[id]); + delete navigator.accelerometer.timers[id]; + } +} + +PhoneGap.addConstructor(function() { + if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer(); +}); +/** + * This class provides access to the device camera. + * @constructor + */ +function Camera() { + +} + +/** + * + * @param {Function} successCallback + * @param {Function} errorCallback + * @param {Object} options + */ +Camera.prototype.getPicture = function(successCallback, errorCallback, options) { + + this.winCallback = successCallback; + this.failCallback = errorCallback; + if (options.quality) + { + GapCam.takePicture(options.quality); + } + else + { + GapCam.takePicture(80); + } +} + +Camera.prototype.win = function(picture) +{ + this.winCallback(picture); +} + +Camera.prototype.fail = function(err) +{ + this.failCallback(err); +} + +PhoneGap.addConstructor(function() { + if (typeof navigator.camera == "undefined") navigator.camera = new Camera(); +}); +/** + * This class provides access to device Compass data. + * @constructor + */ +function Compass() { + /** + * The last known Compass position. + */ + this.lastHeading = null; + + /** + * List of compass watch timers + */ + this.timers = {}; +}; + +Compass.STOPPED = 0; +Compass.STARTING = 1; +Compass.RUNNING = 2; +Compass.ERROR_FAILED_TO_START = 3; +Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"]; + +/** + * Asynchronously aquires the current heading. + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. + * @param {PositionOptions} options The options for getting the heading data such as timeout. + */ +Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) { + + // successCallback required + if (typeof successCallback != "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback != "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + // Get current compass status + var status = CompassHook.getStatus(); + + // If running, then call successCallback + if (status == Compass.RUNNING) { + try { + var heading = CompassHook.getHeading(); + successCallback(heading); + } catch (e) { + console.log("Compass Error in successCallback: " + e); + } + } + + // If not running, then start it + else { + CompassHook.start(); + + // Wait until started + var timer = setInterval(function() { + var status = CompassHook.getStatus(); + if (status != Compass.STARTING) { + clearInterval(timer); + + // If compass is running + if (status == Compass.RUNNING) { + try { + var heading = CompassHook.getHeading(); + successCallback(heading); + } catch (e) { + console.log("Compass Error in successCallback: " + e); + } + } + + // If compass error + else { + console.log("Compass Error: "+ Compass.ERROR_MSG[status]); + try { + if (errorCallback) { + errorCallback(status); + } + } catch (e) { + console.log("Compass Error in errorCallback: " + e); + } + } + } + }, 10); + } +} + +/** + * Asynchronously aquires the heading repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data such as timeout and the frequency of the watch. + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ +Compass.prototype.watchHeading= function(successCallback, errorCallback, options) { + + // Default interval (100 msec) + var frequency = (options != undefined) ? options.frequency : 100; + + // successCallback required + if (typeof successCallback != "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback != "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + // Make sure compass timeout > frequency + 10 sec + var timeout = CompassHook.getTimeout(); + if (timeout < (frequency + 10000)) { + CompassHook.setTimeout(frequency + 10000); // set to frequency + 10 sec + } + + var id = PhoneGap.createUUID(); + CompassHook.start(); + + // Start watch timer + navigator.compass.timers[id] = setInterval(function() { + var status = CompassHook.getStatus(); + + // If compass is running + if (status == Compass.RUNNING) { + try { + var heading = CompassHook.getHeading(); + successCallback(heading); + } catch (e) { + console.log("Compass Error in successCallback: " + e); + } + } + + // If compass had error + else if (status != Compass.STARTING) { + console.log("Compass Error: "+ Compass.ERROR_MSG[status]); + try { + navigator.compass.clearWatch(id); + if (errorCallback) { + errorCallback(status); + } + } catch (e) { + console.log("Compass Error in errorCallback: " + e); + } + } + }, (frequency ? frequency : 1)); + + return id; +} + + +/** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchHeading. + */ +Compass.prototype.clearWatch = function(id) { + + // Stop javascript timer & remove from timer list + if (id && navigator.compass.timers[id]) { + clearInterval(navigator.compass.timers[id]); + delete navigator.compass.timers[id]; + } +} + +PhoneGap.addConstructor(function() { + if (typeof navigator.compass == "undefined") navigator.compass = new Compass(); +}); +var Contact = function(){ + this.name = new ContactName(); + this.emails = []; + this.phones = []; +} + +var ContactName = function() +{ + this.formatted = ""; + this.familyName = ""; + this.givenName = ""; + this.additionalNames = []; + this.prefixes = []; + this.suffixes = []; +} + + +var ContactEmail = function() +{ + this.types = []; + this.address = ""; +} + +var ContactPhoneNumber = function() +{ + this.types = []; + this.number = ""; +} + + +var Contacts = function() +{ + this.records = []; +} + +Contacts.prototype.find = function(obj, win, fail) +{ + if(obj.name != null) + { + // Build up the search term that we'll use in SQL, based on the structure/contents of the contact object passed into find. + var searchTerm = ''; + if (obj.name.givenName && obj.name.givenName.length > 0) { + searchTerm = obj.name.givenName.split(' ').join('%'); + } + if (obj.name.familyName && obj.name.familyName.length > 0) { + searchTerm += obj.name.familyName.split(' ').join('%'); + } + if (!obj.name.familyName && !obj.name.givenName && obj.name.formatted) { + searchTerm = obj.name.formatted; + } + ContactHook.search(searchTerm, "", ""); + } + this.win = win; + this.fail = fail; +} + +Contacts.prototype.droidFoundContact = function(name, npa, email) +{ + var contact = new Contact(); + contact.name = new ContactName(); + contact.name.formatted = name; + contact.name.givenName = name; + var mail = new ContactEmail(); + mail.types.push("home"); + mail.address = email; + contact.emails.push(mail); + phone = new ContactPhoneNumber(); + phone.types.push("home"); + phone.number = npa; + contact.phones.push(phone); + this.records.push(contact); +} + +Contacts.prototype.droidDone = function() +{ + this.win(this.records); +} + +PhoneGap.addConstructor(function() { + if(typeof navigator.contacts == "undefined") navigator.contacts = new Contacts(); +}); +var Crypto = function() +{ +} + +Crypto.prototype.encrypt = function(seed, string, callback) +{ + GapCrypto.encrypt(seed, string); + this.encryptWin = callback; +} + +Crypto.prototype.decrypt = function(seed, string, callback) +{ + GapCrypto.decrypt(seed, string); + this.decryptWin = callback; +} + +Crypto.prototype.gotCryptedString = function(string) +{ + this.encryptWin(string); +} + +Crypto.prototype.getPlainString = function(string) +{ + this.decryptWin(string); +} + +PhoneGap.addConstructor(function() { + if (typeof navigator.Crypto == "undefined") + { + navigator.Crypto = new Crypto(); + } +}); + +/** + * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = PhoneGap.available; + this.platform = null; + this.version = null; + this.name = null; + this.gap = null; + this.uuid = null; + try { + if (window.DroidGap) { + this.available = true; + this.uuid = window.DroidGap.getUuid(); + this.version = window.DroidGap.getOSVersion(); + this.gapVersion = window.DroidGap.getVersion(); + this.platform = window.DroidGap.getPlatform(); + this.name = window.DroidGap.getProductName(); + this.line1Number = window.DroidGap.getLine1Number(); + this.deviceId = window.DroidGap.getDeviceId(); + this.simSerialNumber = window.DroidGap.getSimSerialNumber(); + this.subscriberId = window.DroidGap.getSubscriberId(); + } + } catch(e) { + this.available = false; + } +} + +/* + * You must explicitly override the back button. + */ + +Device.prototype.overrideBackButton = function() +{ + BackButton.override(); +} + +/* + * This resets the back button to the default behaviour + */ + +Device.prototype.resetBackButton = function() +{ + BackButton.reset(); +} + +/* + * This terminates the activity! + */ +Device.prototype.exitApp = function() +{ + BackButton.exitApp(); +} + +PhoneGap.addConstructor(function() { + navigator.device = window.device = new Device(); +}); + + +PhoneGap.addConstructor(function() { if (typeof navigator.fileMgr == "undefined") navigator.fileMgr = new FileMgr();}); + + +/** + * This class provides iPhone read and write access to the mobile device file system. + * Based loosely on http://www.w3.org/TR/2009/WD-FileAPI-20091117/#dfn-empty + */ +function FileMgr() +{ + this.fileWriters = {}; // empty maps + this.fileReaders = {}; + + this.docsFolderPath = "../../Documents"; + this.tempFolderPath = "../../tmp"; + this.freeDiskSpace = -1; + this.getFileBasePaths(); +} + +// private, called from Native Code +FileMgr.prototype._setPaths = function(docs,temp) +{ + this.docsFolderPath = docs; + this.tempFolderPath = temp; +} + +// private, called from Native Code +FileMgr.prototype._setFreeDiskSpace = function(val) +{ + this.freeDiskSpace = val; +} + + +// FileWriters add/remove +// called internally by writers +FileMgr.prototype.addFileWriter = function(filePath,fileWriter) +{ + this.fileWriters[filePath] = fileWriter; +} + +FileMgr.prototype.removeFileWriter = function(filePath) +{ + this.fileWriters[filePath] = null; +} + +// File readers add/remove +// called internally by readers +FileMgr.prototype.addFileReader = function(filePath,fileReader) +{ + this.fileReaders[filePath] = fileReader; +} + +FileMgr.prototype.removeFileReader = function(filePath) +{ + this.fileReaders[filePath] = null; +} + +/******************************************* + * + * private reader callback delegation + * called from native code + */ +FileMgr.prototype.reader_onloadstart = function(filePath,result) +{ + this.fileReaders[filePath].onloadstart(result); +} + +FileMgr.prototype.reader_onprogress = function(filePath,result) +{ + this.fileReaders[filePath].onprogress(result); +} + +FileMgr.prototype.reader_onload = function(filePath,result) +{ + this.fileReaders[filePath].result = unescape(result); + this.fileReaders[filePath].onload(this.fileReaders[filePath].result); +} + +FileMgr.prototype.reader_onerror = function(filePath,err) +{ + this.fileReaders[filePath].result = err; + this.fileReaders[filePath].onerror(err); +} + +FileMgr.prototype.reader_onloadend = function(filePath,result) +{ + this.fileReaders[filePath].onloadend(result); +} + +/******************************************* + * + * private writer callback delegation + * called from native code +*/ +FileMgr.prototype.writer_onerror = function(filePath,err) +{ + this.fileWriters[filePath].onerror(err); +} + +FileMgr.prototype.writer_oncomplete = function(filePath,result) +{ + this.fileWriters[filePath].oncomplete(result); // result contains bytes written +} + + +FileMgr.prototype.getFileBasePaths = function() +{ + //PhoneGap.exec("File.getFileBasePaths"); +} + +FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback) +{ + var test = FileUtil.testFileExists(fileName); + test ? successCallback() : errorCallback(); +} + +FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback) +{ + this.successCallback = successCallback; + this.errorCallback = errorCallback; + var test = FileUtil.testDirectoryExists(dirName); + test ? successCallback() : errorCallback(); +} + +FileMgr.prototype.createDirectory = function(dirName, successCallback, errorCallback) +{ + this.successCallback = successCallback; + this.errorCallback = errorCallback; + var test = FileUtil.createDirectory(dirName); + test ? successCallback() : errorCallback(); +} + +FileMgr.prototype.deleteDirectory = function(dirName, successCallback, errorCallback) +{ + this.successCallback = successCallback; + this.errorCallback = errorCallback; + var test = FileUtil.deleteDirectory(dirName); + test ? successCallback() : errorCallback(); +} + +FileMgr.prototype.deleteFile = function(fileName, successCallback, errorCallback) +{ + this.successCallback = successCallback; + this.errorCallback = errorCallback; + FileUtil.deleteFile(fileName); + test ? successCallback() : errorCallback(); +} + +FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback) +{ + if(this.freeDiskSpace > 0) + { + return this.freeDiskSpace; + } + else + { + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.freeDiskSpace = FileUtil.getFreeDiskSpace(); + (this.freeDiskSpace > 0) ? successCallback() : errorCallback(); + } +} + + +// File Reader + + +function FileReader() +{ + this.fileName = ""; + this.result = null; + this.onloadstart = null; + this.onprogress = null; + this.onload = null; + this.onerror = null; + this.onloadend = null; +} + + +FileReader.prototype.abort = function() +{ + // Not Implemented +} + +FileReader.prototype.readAsText = function(file) +{ + if(this.fileName && this.fileName.length > 0) + { + navigator.fileMgr.removeFileReader(this.fileName,this); + } + this.fileName = file; + navigator.fileMgr.addFileReader(this.fileName,this); + + return FileUtil.read(this.fileName); +} + +// File Writer + +function FileWriter() +{ + this.fileName = ""; + this.result = null; + this.readyState = 0; // EMPTY + this.result = null; + this.onerror = null; + this.oncomplete = null; +} + +FileWriter.prototype.writeAsText = function(file,text,bAppend) +{ + if(this.fileName && this.fileName.length > 0) + { + navigator.fileMgr.removeFileWriter(this.fileName,this); + } + this.fileName = file; + if(bAppend != true) + { + bAppend = false; // for null values + } + navigator.fileMgr.addFileWriter(file,this); + this.readyState = 0; // EMPTY + var call = FileUtil.write(file, text, bAppend); + this.result = null; +} +/** + * This class provides access to device GPS data. + * @constructor + */ +function Geolocation() { + /** + * The last known GPS position. + */ + this.lastPosition = null; + this.lastError = null; + this.listeners = null; +}; + +var geoListeners = []; + +Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) +{ + var position = Geo.getCurrentLocation(); + this.global_success = successCallback; + this.fail = errorCallback; +} + +// Run the global callback +Geolocation.prototype.gotCurrentPosition = function(lat, lng, alt, altacc, head, vel, stamp) +{ + if (lat == "undefined" || lng == "undefined") + { + this.fail(); + } + else + { + coords = new Coordinates(lat, lng, alt, acc, head, vel); + loc = new Position(coords, stamp); + this.lastPosition = loc; + this.global_success(loc); + } +} + +/* +* This turns on the GeoLocator class, which has two listeners. +* The listeners have their own timeouts, and run independently of this process +* In this case, we return the key to the watch hash +*/ + +Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options) +{ + var frequency = (options != undefined)? options.frequency : 10000; + + var key = geoListeners.push( {"success" : successCallback, "fail" : errorCallback }) - 1; + + // TO-DO: Get the names of the method and pass them as strings to the Java. + return Geo.start(frequency, key); +} + +/* + * Retrieve and stop this listener from listening to the GPS + * + */ +Geolocation.prototype.success = function(key, lat, lng, alt, altacc, head, vel, stamp) +{ + var coords = new Coordinates(lat, lng, alt, acc, head, vel); + var loc = new Position(coords, stamp); + geoListeners[key].success(loc); +} + +Geolocation.prototype.fail = function(key) +{ + geoListeners[key].fail(); +} + +Geolocation.prototype.clearWatch = function(watchId) +{ + Geo.stop(watchId); +} + +PhoneGap.addConstructor(function() { + // Taken from Jesse's geo fix (similar problem) in PhoneGap iPhone. Go figure, same browser! + function __proxyObj(origObj, proxyObj, funkList) { + for (var v in funkList) { + origObj[funkList[v]] = proxyObj[funkList[v]]; + } + } + // In the case of Android, we can use the Native Geolocation Object if it exists, so only load this on 1.x devices + if (typeof navigator.geolocation == 'undefined') { + navigator.geolocation = new Geolocation(); + } +}); +function KeyEvent() +{ +} + +KeyEvent.prototype.backTrigger = function() +{ + var e = document.createEvent('Events'); + e.initEvent('backKeyDown'); + document.dispatchEvent(e); +} + +if (document.keyEvent == null || typeof document.keyEvent == 'undefined') +{ + window.keyEvent = document.keyEvent = new KeyEvent(); +} +/** + * This class provides access to the device media, interfaces to both sound and video + * @constructor + */ +function Media(src, successCallback, errorCallback) { + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; +} + +Media.prototype.record = function() { +} + +Media.prototype.play = function() { +} + +Media.prototype.pause = function() { +} + +Media.prototype.stop = function() { +} + + +/** + * This class contains information about any Media errors. + * @constructor + */ +function MediaError() { + this.code = null, + this.message = ""; +} + +MediaError.MEDIA_ERR_ABORTED = 1; +MediaError.MEDIA_ERR_NETWORK = 2; +MediaError.MEDIA_ERR_DECODE = 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; + + +//if (typeof navigator.audio == "undefined") navigator.audio = new Media(src); + +/** + * This class provides access to the device media, interfaces to both sound and video + * @constructor + */ + +Media.prototype.play = function() { + GapAudio.startPlayingAudio(this.src); +} + +Media.prototype.stop = function() { + GapAudio.stopPlayingAudio(); +} + +Media.prototype.startRecord = function() { + GapAudio.startRecordingAudio(this.src); +} + +Media.prototype.stopRecordingAudio = function() { + GapAudio.stopRecordingAudio(); +} + + +/** + * This class contains information about any NetworkStatus. + * @constructor + */ +function NetworkStatus() { + this.code = null; + this.message = ""; +} +NetworkStatus.NOT_REACHABLE = 0; +NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1; +NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2; +/** + * This class provides access to device Network data (reachability). + * @constructor + */ +function Network() { + /** + * The last known Network status. + * { hostName: string, ipAddress: string, + remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) } + */ + this.lastReachability = null; +}; +/** + * Called by the geolocation framework when the reachability status has changed. + * @param {Reachibility} reachability The current reachability status. + */ +Network.prototype.updateReachability = function(reachability) { + this.lastReachability = reachability; +}; +/** + * + * @param {Object} uri + * @param {Function} win + * @param {Object} options (isIpAddress:boolean) + */ +Network.prototype.isReachable = function(uri, win, options) +{ + var status = new NetworkStatus(); + if(NetworkManager.isReachable(uri)) + { + if (NetworkManager.isWifiActive()) { + status.code = NetworkStatus.REACHABLE_VIA_WIFI_NETWORK; + } else { + status.code = NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK; + } + } else { + status.code = NetworkStatus.NOT_REACHABLE; + } + win(status); +}; +PhoneGap.addConstructor(function() { + if (typeof navigator.network == "undefined") navigator.network = new Network(); +});/** + * This class provides access to notifications on the device. + */ +function Notification() { + +} + +/** + * Open a native alert dialog, with a customizable title and button text. + * @param {String} message Message to print in the body of the alert + * @param {String} [title="Alert"] Title of the alert dialog (default: Alert) + * @param {String} [buttonLabel="OK"] Label of the close button (default: OK) + */ +Notification.prototype.alert = function(message, title, buttonLabel) { + // Default is to use a browser alert; this will use "index.html" as the title though + alert(message); +}; + +/** + * Start spinning the activity indicator on the statusbar + */ +Notification.prototype.activityStart = function() { +}; + +/** + * Stop spinning the activity indicator on the statusbar, if it's currently spinning + */ +Notification.prototype.activityStop = function() { +}; + +/** + * Causes the device to blink a status LED. + * @param {Integer} count The number of blinks. + * @param {String} colour The colour of the light. + */ +Notification.prototype.blink = function(count, colour) { + +}; + +/** + * Causes the device to vibrate. + * @param {Integer} mills The number of milliseconds to vibrate for. + */ +Notification.prototype.vibrate = function(mills) { + +}; + +/** + * Causes the device to beep. + * @param {Integer} count The number of beeps. + * @param {Integer} volume The volume of the beep. + */ +Notification.prototype.beep = function(count, volume) { + +}; + +// TODO: of course on Blackberry and Android there notifications in the UI as well + +PhoneGap.addConstructor(function() { + if (typeof navigator.notification == "undefined") navigator.notification = new Notification(); +}); + +Notification.prototype.vibrate = function(mills) +{ + DroidGap.vibrate(mills); +} + +/* + * On the Android, we don't beep, we notify you with your + * notification! We shouldn't keep hammering on this, and should + * review what we want beep to do. + */ + +Notification.prototype.beep = function(count, volume) +{ + DroidGap.beep(count); +} +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} acc + * @param {Object} alt + * @param {Object} altacc + * @param {Object} head + * @param {Object} vel + * @constructor + */ +function Position(coords, timestamp) { + this.coords = coords; + this.timestamp = new Date().getTime(); +} + +function Coordinates(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = alt; + /** + * The direction the device is moving at the position. + */ + this.heading = head; + /** + * The velocity with which the device is moving at the position. + */ + this.speed = vel; + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc != 'undefined') ? altacc : null; +} + +/** + * This class specifies the options for requesting position data. + * @constructor + */ +function PositionOptions() { + /** + * Specifies the desired position accuracy. + */ + this.enableHighAccuracy = true; + /** + * The timeout after which if position data cannot be obtained the errorCallback + * is called. + */ + this.timeout = 10000; +} + +/** + * This class contains information about any GSP errors. + * @constructor + */ +function PositionError() { + this.code = null; + this.message = ""; +} + +PositionError.UNKNOWN_ERROR = 0; +PositionError.PERMISSION_DENIED = 1; +PositionError.POSITION_UNAVAILABLE = 2; +PositionError.TIMEOUT = 3; +PhoneGap.addConstructor(function() { + if (typeof navigator.splashScreen == "undefined") { + navigator.splashScreen = SplashScreen; // SplashScreen object come from native side through addJavaScriptInterface + } +});/* + * This is purely for the Android 1.5/1.6 HTML 5 Storage + * I was hoping that Android 2.0 would deprecate this, but given the fact that + * most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required + */ + +var DroidDB = function() +{ + this.txQueue = []; +} + +DroidDB.prototype.addResult = function(rawdata, tx_id) +{ + eval("var data = " + rawdata); + var tx = this.txQueue[tx_id]; + tx.resultSet.push(data); +} + +DroidDB.prototype.completeQuery = function(tx_id) +{ + var tx = this.txQueue[tx_id]; + var r = new result(); + r.rows.resultSet = tx.resultSet; + r.rows.length = tx.resultSet.length; + tx.win(r); +} + +DroidDB.prototype.fail = function(reason, tx_id) +{ + var tx = this.txQueue[tx_id]; + tx.fail(reason); +} + +var DatabaseShell = function() +{ + +} + +DatabaseShell.prototype.transaction = function(process) +{ + tx = new Tx(); + process(tx); +} + +var Tx = function() +{ + droiddb.txQueue.push(this); + this.id = droiddb.txQueue.length - 1; + this.resultSet = []; +} + +Tx.prototype.executeSql = function(query, params, win, fail) +{ + droidStorage.executeSql(query, params, this.id); + tx.win = win; + tx.fail = fail; +} + +var result = function() +{ + this.rows = new Rows(); +} + +var Rows = function() +{ + this.resultSet = []; + this.length = 0; +} + +Rows.prototype.item = function(row_id) +{ + return this.resultSet[id]; +} + +var dbSetup = function(name, version, display_name, size) +{ + droidStorage.openDatabase(name, version, display_name, size) + db_object = new DatabaseShell(); + return db_object; +} + +PhoneGap.addConstructor(function() { + if (typeof window.openDatabase == "undefined") + { + navigator.openDatabase = window.openDatabase = dbSetup; + window.droiddb = new DroidDB(); + } +}); + diff --git a/framework/bin/classes/com/phonegap/AccelListener.class b/framework/bin/classes/com/phonegap/AccelListener.class new file mode 100644 index 00000000..4d895dcc Binary files /dev/null and b/framework/bin/classes/com/phonegap/AccelListener.class differ diff --git a/framework/bin/classes/com/phonegap/AudioHandler.class b/framework/bin/classes/com/phonegap/AudioHandler.class new file mode 100644 index 00000000..c60429fd Binary files /dev/null and b/framework/bin/classes/com/phonegap/AudioHandler.class differ diff --git a/framework/bin/classes/com/phonegap/AudioPlayer$1.class b/framework/bin/classes/com/phonegap/AudioPlayer$1.class new file mode 100644 index 00000000..9e3149e7 Binary files /dev/null and b/framework/bin/classes/com/phonegap/AudioPlayer$1.class differ diff --git a/framework/bin/classes/com/phonegap/AudioPlayer.class b/framework/bin/classes/com/phonegap/AudioPlayer.class new file mode 100644 index 00000000..9b6e4a29 Binary files /dev/null and b/framework/bin/classes/com/phonegap/AudioPlayer.class differ diff --git a/framework/bin/classes/com/phonegap/BrowserKey.class b/framework/bin/classes/com/phonegap/BrowserKey.class new file mode 100644 index 00000000..1de367c3 Binary files /dev/null and b/framework/bin/classes/com/phonegap/BrowserKey.class differ diff --git a/framework/bin/classes/com/phonegap/CallbackServer.class b/framework/bin/classes/com/phonegap/CallbackServer.class new file mode 100644 index 00000000..580600b5 Binary files /dev/null and b/framework/bin/classes/com/phonegap/CallbackServer.class differ diff --git a/framework/bin/classes/com/phonegap/CameraLauncher.class b/framework/bin/classes/com/phonegap/CameraLauncher.class new file mode 100644 index 00000000..7b6463f7 Binary files /dev/null and b/framework/bin/classes/com/phonegap/CameraLauncher.class differ diff --git a/framework/bin/classes/com/phonegap/CompassListener.class b/framework/bin/classes/com/phonegap/CompassListener.class new file mode 100644 index 00000000..790c0a72 Binary files /dev/null and b/framework/bin/classes/com/phonegap/CompassListener.class differ diff --git a/framework/bin/classes/com/phonegap/ContactManager$ContactTriplet.class b/framework/bin/classes/com/phonegap/ContactManager$ContactTriplet.class new file mode 100644 index 00000000..c84cc234 Binary files /dev/null and b/framework/bin/classes/com/phonegap/ContactManager$ContactTriplet.class differ diff --git a/framework/bin/classes/com/phonegap/ContactManager.class b/framework/bin/classes/com/phonegap/ContactManager.class new file mode 100644 index 00000000..e38d2d6e Binary files /dev/null and b/framework/bin/classes/com/phonegap/ContactManager.class differ diff --git a/framework/bin/classes/com/phonegap/CryptoHandler.class b/framework/bin/classes/com/phonegap/CryptoHandler.class new file mode 100644 index 00000000..725a45f0 Binary files /dev/null and b/framework/bin/classes/com/phonegap/CryptoHandler.class differ diff --git a/framework/bin/classes/com/phonegap/Device.class b/framework/bin/classes/com/phonegap/Device.class new file mode 100644 index 00000000..1cff488c Binary files /dev/null and b/framework/bin/classes/com/phonegap/Device.class differ diff --git a/framework/bin/classes/com/phonegap/DirectoryManager.class b/framework/bin/classes/com/phonegap/DirectoryManager.class new file mode 100644 index 00000000..3acc4a37 Binary files /dev/null and b/framework/bin/classes/com/phonegap/DirectoryManager.class differ diff --git a/framework/bin/classes/com/phonegap/DroidGap$1.class b/framework/bin/classes/com/phonegap/DroidGap$1.class new file mode 100644 index 00000000..30f77408 Binary files /dev/null and b/framework/bin/classes/com/phonegap/DroidGap$1.class differ diff --git a/framework/bin/classes/com/phonegap/DroidGap$EclairClient.class b/framework/bin/classes/com/phonegap/DroidGap$EclairClient.class new file mode 100644 index 00000000..1fa71b68 Binary files /dev/null and b/framework/bin/classes/com/phonegap/DroidGap$EclairClient.class differ diff --git a/framework/bin/classes/com/phonegap/DroidGap$GapClient$GapCancelDialog.class b/framework/bin/classes/com/phonegap/DroidGap$GapClient$GapCancelDialog.class new file mode 100644 index 00000000..df7c7cd8 Binary files /dev/null and b/framework/bin/classes/com/phonegap/DroidGap$GapClient$GapCancelDialog.class differ diff --git a/framework/bin/classes/com/phonegap/DroidGap$GapClient$GapOKDialog.class b/framework/bin/classes/com/phonegap/DroidGap$GapClient$GapOKDialog.class new file mode 100644 index 00000000..bbc2384e Binary files /dev/null and b/framework/bin/classes/com/phonegap/DroidGap$GapClient$GapOKDialog.class differ diff --git a/framework/bin/classes/com/phonegap/DroidGap$GapClient.class b/framework/bin/classes/com/phonegap/DroidGap$GapClient.class new file mode 100644 index 00000000..9a5718c4 Binary files /dev/null and b/framework/bin/classes/com/phonegap/DroidGap$GapClient.class differ diff --git a/framework/bin/classes/com/phonegap/DroidGap$GapViewClient.class b/framework/bin/classes/com/phonegap/DroidGap$GapViewClient.class new file mode 100644 index 00000000..3ac57246 Binary files /dev/null and b/framework/bin/classes/com/phonegap/DroidGap$GapViewClient.class differ diff --git a/framework/bin/classes/com/phonegap/DroidGap.class b/framework/bin/classes/com/phonegap/DroidGap.class new file mode 100644 index 00000000..40398327 Binary files /dev/null and b/framework/bin/classes/com/phonegap/DroidGap.class differ diff --git a/framework/bin/classes/com/phonegap/FileUtils.class b/framework/bin/classes/com/phonegap/FileUtils.class new file mode 100644 index 00000000..fec1838f Binary files /dev/null and b/framework/bin/classes/com/phonegap/FileUtils.class differ diff --git a/framework/bin/classes/com/phonegap/GeoBroker.class b/framework/bin/classes/com/phonegap/GeoBroker.class new file mode 100644 index 00000000..4c6ce178 Binary files /dev/null and b/framework/bin/classes/com/phonegap/GeoBroker.class differ diff --git a/framework/bin/classes/com/phonegap/GeoListener.class b/framework/bin/classes/com/phonegap/GeoListener.class new file mode 100644 index 00000000..c9baa8bf Binary files /dev/null and b/framework/bin/classes/com/phonegap/GeoListener.class differ diff --git a/framework/bin/classes/com/phonegap/GpsListener.class b/framework/bin/classes/com/phonegap/GpsListener.class new file mode 100644 index 00000000..e7ad8f57 Binary files /dev/null and b/framework/bin/classes/com/phonegap/GpsListener.class differ diff --git a/framework/bin/classes/com/phonegap/HttpHandler.class b/framework/bin/classes/com/phonegap/HttpHandler.class new file mode 100644 index 00000000..824e800a Binary files /dev/null and b/framework/bin/classes/com/phonegap/HttpHandler.class differ diff --git a/framework/bin/classes/com/phonegap/NetworkListener.class b/framework/bin/classes/com/phonegap/NetworkListener.class new file mode 100644 index 00000000..49fd525a Binary files /dev/null and b/framework/bin/classes/com/phonegap/NetworkListener.class differ diff --git a/framework/bin/classes/com/phonegap/NetworkManager.class b/framework/bin/classes/com/phonegap/NetworkManager.class new file mode 100644 index 00000000..6103b417 Binary files /dev/null and b/framework/bin/classes/com/phonegap/NetworkManager.class differ diff --git a/framework/bin/classes/com/phonegap/R$attr.class b/framework/bin/classes/com/phonegap/R$attr.class new file mode 100644 index 00000000..2de60c7c Binary files /dev/null and b/framework/bin/classes/com/phonegap/R$attr.class differ diff --git a/framework/bin/classes/com/phonegap/R$drawable.class b/framework/bin/classes/com/phonegap/R$drawable.class new file mode 100644 index 00000000..32867730 Binary files /dev/null and b/framework/bin/classes/com/phonegap/R$drawable.class differ diff --git a/framework/bin/classes/com/phonegap/R$id.class b/framework/bin/classes/com/phonegap/R$id.class new file mode 100644 index 00000000..ee8e8e50 Binary files /dev/null and b/framework/bin/classes/com/phonegap/R$id.class differ diff --git a/framework/bin/classes/com/phonegap/R$layout.class b/framework/bin/classes/com/phonegap/R$layout.class new file mode 100644 index 00000000..90da8677 Binary files /dev/null and b/framework/bin/classes/com/phonegap/R$layout.class differ diff --git a/framework/bin/classes/com/phonegap/R$string.class b/framework/bin/classes/com/phonegap/R$string.class new file mode 100644 index 00000000..1078074d Binary files /dev/null and b/framework/bin/classes/com/phonegap/R$string.class differ diff --git a/framework/bin/classes/com/phonegap/R.class b/framework/bin/classes/com/phonegap/R.class new file mode 100644 index 00000000..4bfa3997 Binary files /dev/null and b/framework/bin/classes/com/phonegap/R.class differ diff --git a/framework/bin/classes/com/phonegap/SimpleCrypto.class b/framework/bin/classes/com/phonegap/SimpleCrypto.class new file mode 100644 index 00000000..aa118458 Binary files /dev/null and b/framework/bin/classes/com/phonegap/SimpleCrypto.class differ diff --git a/framework/bin/classes/com/phonegap/SplashScreen$1.class b/framework/bin/classes/com/phonegap/SplashScreen$1.class new file mode 100644 index 00000000..fe78aa2a Binary files /dev/null and b/framework/bin/classes/com/phonegap/SplashScreen$1.class differ diff --git a/framework/bin/classes/com/phonegap/SplashScreen.class b/framework/bin/classes/com/phonegap/SplashScreen.class new file mode 100644 index 00000000..f2b1ddee Binary files /dev/null and b/framework/bin/classes/com/phonegap/SplashScreen.class differ diff --git a/framework/bin/classes/com/phonegap/StandAlone.class b/framework/bin/classes/com/phonegap/StandAlone.class new file mode 100644 index 00000000..3c9e2829 Binary files /dev/null and b/framework/bin/classes/com/phonegap/StandAlone.class differ diff --git a/framework/bin/classes/com/phonegap/Storage.class b/framework/bin/classes/com/phonegap/Storage.class new file mode 100644 index 00000000..069efbc6 Binary files /dev/null and b/framework/bin/classes/com/phonegap/Storage.class differ diff --git a/framework/bin/classes/com/phonegap/TempListener.class b/framework/bin/classes/com/phonegap/TempListener.class new file mode 100644 index 00000000..cae86550 Binary files /dev/null and b/framework/bin/classes/com/phonegap/TempListener.class differ diff --git a/framework/bin/classes/com/phonegap/WebViewReflect.class b/framework/bin/classes/com/phonegap/WebViewReflect.class new file mode 100644 index 00000000..91e3a62a Binary files /dev/null and b/framework/bin/classes/com/phonegap/WebViewReflect.class differ diff --git a/framework/bin/classes/com/phonegap/api/Command.class b/framework/bin/classes/com/phonegap/api/Command.class new file mode 100644 index 00000000..49398b2f Binary files /dev/null and b/framework/bin/classes/com/phonegap/api/Command.class differ diff --git a/framework/bin/classes/com/phonegap/api/CommandManager$1.class b/framework/bin/classes/com/phonegap/api/CommandManager$1.class new file mode 100644 index 00000000..36f2695b Binary files /dev/null and b/framework/bin/classes/com/phonegap/api/CommandManager$1.class differ diff --git a/framework/bin/classes/com/phonegap/api/CommandManager.class b/framework/bin/classes/com/phonegap/api/CommandManager.class new file mode 100644 index 00000000..1eca97bb Binary files /dev/null and b/framework/bin/classes/com/phonegap/api/CommandManager.class differ diff --git a/framework/bin/classes/com/phonegap/api/CommandResult$Status.class b/framework/bin/classes/com/phonegap/api/CommandResult$Status.class new file mode 100644 index 00000000..114598fc Binary files /dev/null and b/framework/bin/classes/com/phonegap/api/CommandResult$Status.class differ diff --git a/framework/bin/classes/com/phonegap/api/CommandResult.class b/framework/bin/classes/com/phonegap/api/CommandResult.class new file mode 100644 index 00000000..9c08138b Binary files /dev/null and b/framework/bin/classes/com/phonegap/api/CommandResult.class differ diff --git a/lib/generate.rb b/lib/generate.rb new file mode 100644 index 00000000..af1a9d36 --- /dev/null +++ b/lib/generate.rb @@ -0,0 +1,25 @@ +# ProjectName +# | +# |-tmp ......... Temporary directory for generated projects to launch into emulators or devices. Ignore. +# | '-android ... A generated Android project. +# | +# |-opt ......... Optional platform specific code. Plugins install native code here. +# | |-android ... Java files +# | '-iphone .... Objective C +# | +# |-bin ......... Generated applications. +# | '-android ... project.apk +# | +# '-www ......... html, css and javascript (optional config.xml for additional properties) +# +class Generate + def initialize(name) + if name.nil? + puts "You need to supply a name to generate a project. Try this:\n\ndroidgap gen MyApp\n\n" + return + end + from = File.join ROOT, "example" + to = File.join FileUtils.pwd, name + FileUtils.cp_r from, to + end +end \ No newline at end of file diff --git a/lib/jruby-complete-1.4.0RC1.jar b/lib/jruby-complete-1.4.0RC1.jar new file mode 100644 index 00000000..db0e1bef Binary files /dev/null and b/lib/jruby-complete-1.4.0RC1.jar differ diff --git a/lib/package.rb b/lib/package.rb new file mode 100644 index 00000000..192964ba --- /dev/null +++ b/lib/package.rb @@ -0,0 +1,206 @@ +# Package +# +# Generates an Android project from a valid PhoneGap project directory and puts it in ../[PROJECT NAME]-android +# +# TODO ensure the phonegap.js file is overwritten every single time into the correct tmp dir +# +class Package + attr_reader :name, :pkg, :www, :path + + def initialize(path) + read_config(path) + clobber + build_jar + create_android + include_www + generate_manifest + copy_libs + add_name_to_strings + write_java + end + + + def read_config(path) + # if no path is supplied uses current directory for project + path = FileUtils.pwd if path.nil? + # if a www is found use it for the project + path = File.join(path, 'www') if File.exists? File.join(path, 'www') + # ensure an index.html + raise 'No index.html found!' unless File.exists? File.join(path, 'index.html') + + # setup default vars + @name = path.split("/").last + @path = File.join(path, '..', "#{ name }-android") # File.join(path, "tmp", "android") + @www = path # File.join(path, 'www') + @name = path.split('/').last + @pkg = "com.phonegap.tmp#{ Time.now.usec }" # ensure a unique pkg + + # android sdk discovery ... could be better + @android_sdk_path = `which android`.gsub('/tools/android','') + @android_dir = File.expand_path(File.dirname(__FILE__).gsub('lib','')) + @framework_dir = File.join(@android_dir, "framework") + + # read in www/config.xml and kick off package + @config = {} + config_file = File.join(@www, 'config.xml') + + if File.exists?(config_file) + require 'rexml/document' + f = File.new config_file + doc = REXML::Document.new(f) + + @config[:id] = doc.root.attributes["id"] + @config[:version] = doc.root.attributes["version"] + + doc.root.elements.each do |n| + @config[:name] = n.text if n.name == 'name' + @config[:description] = n.text if n.name == 'description' + @config[:icon] = n.attributes["src"] if n.name == 'icon' + @config[:content] = n.attributes["src"] if n.name == 'content' + + if n.name == "preference" && n.attributes["name"] == 'javascript_folder' + @config[:js_dir] = n.attributes["value"] + end + end + + # extract android specific stuff + @config[:versionCode] = doc.elements["//android:versionCode"] ? doc.elements["//android:versionCode"].text : 3 + @config[:minSdkVersion] = doc.elements["//android:minSdkVersion"] ? doc.elements["//android:minSdkVersion"].text : 1 + + # will change the name from the directory to the name element text + @name = @config[:name] if @config[:name] + + # set the icon from the config + @icon = File.join(@www, @config[:icon]) + + # sets the app js dir where phonegap.js gets copied + @app_js_dir = @config[:js_dir] ? @config[:js_dir] : '' + + # sets the start page + @content = @config[:content] ? @config[:content] : 'index.html' + else + # set to the default icon location if not in config + @icon = File.join(@www, 'icon.png') + @app_js_dir = '' + @content = 'index.html' + end + end + + # kills and replaces tmp/android + def clobber + FileUtils.rm_r(@path) if File.exists? @path + FileUtils.mkdir_p @path + end + + # removes local.properties and recreates based on android_sdk_path + # then generates framework/phonegap.jar + def build_jar + %w(local.properties phonegap.js phonegap.jar).each do |f| + FileUtils.rm File.join(@framework_dir, f) if File.exists? File.join(@framework_dir, f) + end + open(File.join(@framework_dir, "local.properties"), 'w') do |f| + f.puts "sdk.dir=#{ @android_sdk_path }" + end + Dir.chdir(@framework_dir) + `ant jar` + Dir.chdir(@android_dir) + end + + # runs android create project + # TODO need to allow more flexible SDK targetting via config.xml + def create_android + target_id = `android list targets | grep id:`.split("\n").last.match(/\d/).to_a.first + `android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name.gsub(' ','') } -p #{ @path }` + end + + # copies the project/www folder into tmp/android/www + def include_www + FileUtils.mkdir_p File.join(@path, "assets", "www") + FileUtils.cp_r File.join(@www, "."), File.join(@path, "assets", "www") + end + + # creates an AndroidManifest.xml for the project + def generate_manifest + manifest = "" + open(File.join(@framework_dir, "AndroidManifest.xml"), 'r') do |old| + manifest = old.read + manifest.gsub! 'android:versionCode="5"', 'android:versionCode="1"' + manifest.gsub! 'package="com.phonegap"', "package=\"#{ @pkg }\"" + manifest.gsub! 'android:name=".StandAlone"', "android:name=\".#{ @name.gsub(' ','') }\"" + manifest.gsub! 'android:minSdkVersion="5"', 'android:minSdkVersion="3"' + end + open(File.join(@path, "AndroidManifest.xml"), 'w') { |x| x.puts manifest } + end + + # copies stuff from src directory into the android project directory (@path) + def copy_libs + framework_res_dir = File.join(@framework_dir, "res") + app_res_dir = File.join(@path, "res") + # copies in the jar + FileUtils.mkdir_p File.join(@path, "libs") + FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs") + # copies in the strings.xml + FileUtils.mkdir_p File.join(app_res_dir, "values") + FileUtils.cp File.join(framework_res_dir, "values","strings.xml"), File.join(app_res_dir, "values", "strings.xml") + # drops in the layout files: main.xml and preview.xml + FileUtils.mkdir_p File.join(app_res_dir, "layout") + %w(main.xml).each do |f| + FileUtils.cp File.join(framework_res_dir, "layout", f), File.join(app_res_dir, "layout", f) + end + # icon file copy + # if it is not in the www directory use the default one in the src dir + @icon = File.join(framework_res_dir, "drawable", "icon.png") unless File.exists?(@icon) + %w(drawable-hdpi drawable-ldpi drawable-mdpi).each do |e| + FileUtils.mkdir_p(File.join(app_res_dir, e)) + FileUtils.cp(@icon, File.join(app_res_dir, e, "icon.png")) + end + # concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir + js_dir = File.join(@framework_dir, "assets", "js") + phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base')) + Dir.new(js_dir).entries.each do |script| + next if script[0].chr == "." or script == "phonegap.js.base" + phonegapjs << IO.read(File.join(js_dir, script)) + phonegapjs << "\n\n" + end + File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap.js"), 'w') {|f| f.write(phonegapjs) } + end + + # puts app name in strings + def add_name_to_strings + x = " + + #{ @name } + Snap + + " + open(File.join(@path, "res", "values", "strings.xml"), 'w') do |f| + f.puts x.gsub(' ','') + end + end + + # this is so fucking unholy yet oddly beautiful + # not sure if I should thank Ruby or apologize for this abusive use of string interpolation + def write_java + j = " + package #{ @pkg }; + + import android.app.Activity; + import android.os.Bundle; + import com.phonegap.*; + + public class #{ @name.gsub(' ','') } extends DroidGap + { + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + super.loadUrl(\"file:///android_asset/www/#{ @content }\"); + } + } + " + code_dir = File.join(@path, "src", @pkg.gsub('.', File::SEPARATOR)) + FileUtils.mkdir_p(code_dir) + open(File.join(code_dir, "#{ @name.gsub(' ','') }.java"),'w') { |f| f.puts j.gsub(' ','') } + end + # +end diff --git a/lib/run.rb b/lib/run.rb new file mode 100644 index 00000000..9b18961c --- /dev/null +++ b/lib/run.rb @@ -0,0 +1,67 @@ +# +# Run +# --- +# +# A handy machine that does the following: +# +# - packages www to a valid android project in tmp/android +# - builds tmp/android project into an apk +# - installs apk onto first device found +# - if there is no device attached it will start an emulator with the first avd found +# - TODO install apk into now running emulator... need to find way to wait for it to have started +# - TODO if no avds present it will attempt to create one +# +class Run + # if no path is supplied uses current directory for project + def initialize(path) + puts 'packaging www as phonegap/android project in ./tmp/android...' + @pkg = Package.new(path) + @apk = File.join(@pkg.path, "bin", "#{ @pkg.name.gsub(' ','') }-debug.apk") + build_apk + first_device.nil? ? start_emulator : install_to_device + end + + def build_apk + puts 'building apk...' + `cd #{ @pkg.path }; ant debug;` + end + + def install_to_device + puts 'installing to device...' + `cd #{ @pkg.path }; ant install;` + end + + def start_emulator + puts "No devices attached. Starting emulator w/ first avd...\n" + $stdout.sync = true + IO.popen("emulator -avd #{ first_avd } -logcat all") do |f| + until f.eof? + puts f.gets + if f.gets.include? 'Boot is finished' + #IO.popen("cd #{ @pkg.path }; ant install;") do |f| + # puts f.gets + #end + puts "\n\nEMULATOR IS NOW RUNNING!\n\n" + puts "install your app by running: " + puts "cd #{ @pkg.path }; ant install;" + end + end + end + end + + # helpers + def first_device + fd = `adb devices`.split("\n").pop() + if fd == 'List of devices attached ' + nil + else + fd.gsub('device','') + end + end + + def first_avd + `android list avd | grep "Name: "`.gsub('Name: ','').strip + end + + # +end \ No newline at end of file diff --git a/updategap b/lib/update.rb old mode 100755 new mode 100644 similarity index 62% rename from updategap rename to lib/update.rb index 53db4a7e..2a64c9a7 --- a/updategap +++ b/lib/update.rb @@ -1,22 +1,18 @@ #!/usr/bin/env ruby require 'fileutils' -# ./droidgap /Users/brianleroux/Code/android-sdk-mac MyApp com.westcoastlogic example /Users/brianleroux/Desktop/MyApp -class Build +class Update attr_reader :android_sdk_path, :path - def initialize(*a) - @android_sdk_path, @path = a - @android_dir = File.expand_path(File.dirname(__FILE__)) - @framework_dir = File.join(@android_dir, "framework") - end - - # runs the build script - def run + def initialize + @path = FileUtils.pwd + @android_sdk_path = `which android`.gsub('/tools/android','') + @android_dir = File.expand_path(File.dirname(__FILE__)) + @framework_dir = File.join(@android_dir, "..", "framework") + # puts "updating #{ @path } with phonegap from #{ @android_dir }" build_jar copy_libs - puts "Complete!" - end + end # removes local.properties and recreates based on android_sdk_path # then generates framework/phonegap.jar @@ -55,27 +51,4 @@ class Build File.open(File.join(@path, "assets", "www", "phonegap.js"), 'w') {|f| f.write(phonegapjs) } end # -end - - -if ARGV.length == 2 - Build.new(*ARGV).run -else - puts <<-EOF - - TestGap: Builds the JS and PhoneGap Android jar file and copies them to your project. - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Creates a fresh app for hybrid mobile web hacking. Delicious robot! - - Usage: - - ./testgap - - Params: - - android_sdk_path ... The path to your Android SDK install. - path ............... The path to generate the Android application. - - EOF end \ No newline at end of file