CFXIXI工作室首页
CF西西的博客 | phonegap

【转】浅析 Cordova for iOS

7. 六月 2013
转自:http://www.cocoachina.com/applenews/devnews/2013/0520/6238.html Cordova,对这个名字大家可能比较陌生,大家肯定听过 PhoneGap 这个名字,Cordova 就是 PhoneGap 被 Adobe 收购后所改的名字。(Cordova网址以及框架下载地址:http://cordova.apache.org/)   Cordova 是一个可以让 JS 与原生代码(包括 Android 的 java,iOS 的 Objective-C 等)互相通信的一个库,并且提供了一系列的插件类,比如 JS 直接操作本地数据库的插件类。   这些插件类都是基于 JS 与 Objective-C 可以互相通信的基础的,这篇文章说说 Cordova 是如何做到 JS 与 Objective-C 互相通信的,解释如何互相通信需要弄清楚下面三个问题:   一、JS 怎么跟 Objective-C 通信?二、Objective-C 怎么跟 JS 通信?三、JS 请求 Objective-C,Objective-C 返回结果给 JS,这一来一往是怎么串起来的? Cordova 现在最新版本是 2.7.0,本文也是基于 2.7.0 版本进行分析的。   一、JS 怎么跟 Objective-C 通信 JS 与 Objetive-C 通信的关键代码如下:(点击代码框右上角的文件名链接,可直接跳转该文件在 github 的地址)                                            JS 发起请求                                                         cordova.js (github 地址)function iOSExec() {  ...  if (!isInContextOfEvalJs && commandQueue.length == 1)  {      // 如果支持 XMLHttpRequest,则使用 XMLHttpRequest 方式      if (bridgeMode != jsToNativeModes.IFRAME_NAV) {            // This prevents sending an XHR when there is already one being sent.            // This should happen only in rare circumstances (refer to unit tests).            if (execXhr && execXhr.readyState != 4) {                execXhr = null;            }            // Re-using the XHR improves exec() performance by about 10%.            execXhr = execXhr || new XMLHttpRequest();            // Changing this to a GET will make the XHR reach the URIProtocol on 4.2.            // For some reason it still doesn't work though...            // Add a timestamp to the query param to prevent caching.            execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);            if (!vcHeaderValue) {                vcHeaderValue = /.*\((.*)\)/.exec(navigator.userAgent)[1];            }            execXhr.setRequestHeader('vc', vcHeaderValue);            execXhr.setRequestHeader('rc', ++requestCount);            if (shouldBundleCommandJson()) {              // 设置请求的数据                execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());            }            // 发起请求            execXhr.send(null);        } else {          // 如果不支持 XMLHttpRequest,则使用透明 iframe 的方式,设置 iframe 的 src 属性            execIframe = execIframe || createExecIframe();            execIframe.src = "gap://ready";        }    }  ...} JS 使用了两种方式来与 Objective-C 通信,一种是使用 XMLHttpRequest 发起请求的方式,另一种则是通过设置透明的 iframe 的 src 属性,下面详细介绍一下两种方式是怎么工作的:   XMLHttpRequest bridge JS 端使用 XMLHttpRequest 发起了一个请求:execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true); ,请求的地址是 /!gap_exec;   并把请求的数据放在了请求的 header 里面,见这句代码:execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages()); 。   而在 Objective-C 端使用一个 NSURLProtocol 的子类来检查每个请求,如果地址是 /!gap_exec 的话,则认为是 Cordova 通信的请求,直接拦截,拦截后就可以通过分析请求的数据,分发到不同的插件类(CDVPlugin 类的子类)的方法中:                                          UCCDVURLProtocol 拦截请求                             UCCDVURLProtocol.m (github 地址)+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest{    NSURL* theUrl = [theRequest URL];    NSString* theScheme = [theUrl scheme];   // 判断请求是否为 /!gap_exec    if ([[theUrl path] isEqualToString:@"/!gap_exec"]) {        NSString* viewControllerAddressStr = [theRequest valueForHTTPHeaderField:@"vc"];        if (viewControllerAddressStr == nil) {            NSLog(@"!cordova request missing vc header");            return NO;        }        long long viewControllerAddress = [viewControllerAddressStr longLongValue];        // Ensure that the UCCDVViewController has not been dealloc'ed.        UCCDVViewController* viewController = nil;        @synchronized(gRegisteredControllers) {            if (![gRegisteredControllers containsObject:                  [NSNumber numberWithLongLong:viewControllerAddress]]) {                return NO;            }            viewController = (UCCDVViewController*)(void*)viewControllerAddress;        }       // 获取请求的数据        NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"];        NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"];        if (requestId == nil) {            NSLog(@"!cordova request missing rc header");            return NO;        }          ...    }    ...} Cordova 中优先使用这种方式,Cordova.js 中的注释有提及为什么优先使用 XMLHttpRequest 的方式,及为什么保留第二种 iframe bridge 的通信方式: // XHR mode does not work on iOS 4.2, so default to IFRAME_NAV for such devices.// XHR mode’s main advantage is working around a bug in -webkit-scroll, which// doesn’t exist in 4.X devices anyways   iframe bridge 在 JS 端创建一个透明的 iframe,设置这个 ifame 的 src 为自定义的协议,而 ifame 的 src 更改时,UIWebView 会先回调其 delegate 的 webView:shouldStartLoadWithRequest:navigationType: 方法,关键代码如下:                                           UIWebView拦截加载                                         CDVViewController.m(github 地址) // UIWebView 加载 URL 前回调的方法,返回 YES,则开始加载此 URL,返回 NO,则忽略此 URL- (BOOL)webView:(UIWebView*)theWebView          shouldStartLoadWithRequest:(NSURLRequest*)request          navigationType:(UIWebViewNavigationType)navigationType{    NSURL* url = [request URL];     /*     * Execute any commands queued with cordova.exec() on the JS side.     * The part of the URL after gap:// is irrelevant.     */    // 判断是否 Cordova 的请求,对于 JS 代码中 execIframe.src = "gap://ready" 这句    if ([[url scheme] isEqualToString:@"gap"]) {        // 获取请求的数据,并对数据进行分析、处理        [_commandQueue fetchCommandsFromJs];        return NO;    }    ...} 二、Objective-C 怎么跟 JS 通信 熟悉 UIWebView 用法的同学都知道 UIWebView 有一个这样的方法 stringByEvaluatingJavaScriptFromString:,这个方法可以让一个 UIWebView 对象执行一段 JS 代码,这样就可以达到 Objective-C 跟 JS 通信的效果,在 Cordova 的代码中多处用到了这个方法,其中最重要的两处如下:   获取 JS 的请求数据                                           获取 JS 的请求数据                                          CDVCommandQueue.m(github 地址) - (void)fetchCommandsFromJs{    // Grab all the queued commands from the JS side.    NSString* queuedCommandsJSON = [_viewController.webView                                      stringByEvaluatingJavaScriptFromString:                                          @"cordova.require('cordova/exec').nativeFetchMessages()"];     [self enqueCommandBatch:queuedCommandsJSON];    if ([queuedCommandsJSON length] > 0) {        CDV_EXEC_LOG(@"Exec: Retrieved new exec messages by request.");    }} 把 JS 请求的结果返回给 JS 端                                          把 JS 请求的结果返回给 JS 端                          CDVCommandDelegateImpl.m(github 地址)- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop{    js = [NSString stringWithFormat:                  @"cordova.require('cordova/exec').nativeEvalAndFetch(function(){ %@ })",                  js];    if (scheduledOnRunLoop) {        [self evalJsHelper:js];    } else {        [self evalJsHelper2:js];    }} - (void)evalJsHelper2:(NSString*)js{    CDV_EXEC_LOG(@"Exec: evalling: %@", [js substringToIndex:MIN([js length], 160)]);    NSString* commandsJSON = [_viewController.webView                              stringByEvaluatingJavaScriptFromString:js];    if ([commandsJSON length] > 0) {        CDV_EXEC_LOG(@"Exec: Retrieved new exec messages by chaining.");    }     [_commandQueue enqueCommandBatch:commandsJSON];} - (void)evalJsHelper:(NSString*)js{    // Cycle the run-loop before executing the JS.    // This works around a bug where sometimes alerts() within callbacks can cause    // dead-lock.    // If the commandQueue is currently executing, then we know that it is safe to    // execute the callback immediately.    // Using    (dispatch_get_main_queue()) does *not* fix deadlocks for some reaon,    // but performSelectorOnMainThread: does.    if (![NSThread isMainThread] || !_commandQueue.currentlyExecuting) {        [self performSelectorOnMainThread:@selector(evalJsHelper2:)                              withObject:js                           waitUntilDone:NO];    } else {        [self evalJsHelper2:js];    }} 三、怎么串起来 先看一下 Cordova JS 端请求方法的格式: // successCallback : 成功回调方法// failCallback    : 失败回调方法// server          : 所要请求的服务名字// action          : 所要请求的服务具体操作// actionArgs      : 请求操作所带的参数cordova.exec(successCallback, failCallback, service, action, actionArgs);   传进来的这五个参数并不是直接传送给原生代码的,Cordova JS 端会做以下的处理:   1.会为每个请求生成一个叫 callbackId 的唯一标识:这个参数需传给 Objective-C 端,Objective-C 处理完后,会把 callbackId 连同处理结果一起返回给 JS 端。2.以 callbackId 为 key,{success:successCallback, fail:failCallback} 为 value,把这个键值对保存在 JS 端的字典里,successCallback 与 failCallback 这两个参数不需要传给 Objective-C 端,Objective-C 返回结果时带上 callbackId,JS 端就可以根据 callbackId 找到回调方法。3.每次 JS 请求,最后发到 Objective-C 的数据包括:callbackId, service, action, actionArgs。 关键代码如下:                                            JS 端处理请求                                                     cordova.js(github 地址)function iOSExec() {    ...  // 生成一个 callbackId 的唯一标识,并把此标志与成功、失败回调方法一起保存在 JS 端    // Register the callbacks and add the callbackId to the positional    // arguments if given.    if (successCallback || failCallback) {        callbackId = service + cordova.callbackId++;        cordova.callbacks[callbackId] =            {success:successCallback, fail:failCallback};    }     actionArgs = massageArgsJsToNative(actionArgs);   // 把 callbackId,service,action,actionArgs 保持到 commandQueue 中  // 这四个参数就是最后发给原生代码的数据    var command = [callbackId, service, action, actionArgs];    commandQueue.push(JSON.stringify(command));    ...} // 获取请求的数据,包括 callbackId, service, action, actionArgsiOSExec.nativeFetchMessages = function() {    // Each entry in commandQueue is a JSON string already.    if (!commandQueue.length) {        return '';    }    var json = '[' + commandQueue.join(',') + ']';    commandQueue.length = 0;    return json;}; 原生代码拿到 callbackId、service、action 及 actionArgs 后,会做以下的处理:   1.根据 service 参数找到对应的插件类2.根据 action 参数找到插件类中对应的处理方法,并把 actionArgs 作为处理方法请求参数的一部分传给处理方法3.处理完成后,把处理结果及 callbackId 返回给 JS 端,JS 端收到后会根据 callbackId 找到回调方法,并把处理结果传给回调方法 关键代码:                                              Objective-C 返回结果给JS端                           CDVCommandDelegateImpl.m(github 地址)- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId{    CDV_EXEC_LOG(@"Exec(%@): Sending result. Status=%@", callbackId, result.status);    // This occurs when there is are no win/fail callbacks for the call.    if ([@"INVALID" isEqualToString : callbackId]) {        return;    }    int status = [result.status intValue];    BOOL keepCallback = [result.keepCallback boolValue];    NSString* argumentsAsJSON = [result argumentsAsJSON];   // 将请求的处理结果及 callbackId 通过调用 JS 方法返回给 JS 端    NSString* js = [NSString stringWithFormat:                              @"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d)",                              callbackId, status, argumentsAsJSON, keepCallback];     [self evalJsHelper:js];}                                              JS 端根据 callbackId 回调                              cordova.js(github 地址) // 根据 callbackId 及是否成功标识,找到回调方法,并把处理结果传给回调方法 callbackFromNative: function(callbackId, success, status, args, keepCallback) {        var callback = cordova.callbacks[callbackId];        if (callback) {            if (success && status == cordova.callbackStatus.OK) {                callback.success && callback.success.apply(null, args);            } else if (!success) {                callback.fail && callback.fail.apply(null, args);            }             // Clear callback if not expecting any more results            if (!keepCallback) {                delete cordova.callbacks[callbackId];            }        }    } 通信效率 Cordova 这套通信效率并不算低。我使用 iPod Touch 4 与 iPhone 5 进行真机测试:JS 做一次请求,Objective-C 收到请求后不做任何的处理,马上把请求的数据返回给 JS 端,这样能大概的测出一来一往的时间(从 JS 发出请求,到 JS 收到结果的时间)。每个真机我做了三组测试,每组连续测试十次,每组测试前我都会把机器重启,结果如下: iPod Touch 4(时间单位:毫秒): 这三十次测试的平均时间是:(11.0 + 15.2 + 13.2) / 3 = 13.13 毫秒 iPhone 5(时间单位:毫秒) 这三十次测试的平均时间是:(2.7 + 2.8 + 2.7) / 3 = 2.73 毫秒   这通信的效率虽然比不上原生调原生,但是也是属于可接受的范围了。 Cordova网址以及框架下载地址:http://cordova.apache.org/

phonegap

微信PhoneGap插件

7. 五月 2013
PhoneGap Android 交流 QQ 群 250395324欢迎您的加入 。 Android版: https://github.com/sinacloud/phonegap-plugins/tree/master/Android/WeixinIOS版:https://github.com/sinacloud/phonegap-plugins/tree/master/iOS/Weixin感谢 qhm123,还有草丁同学 插件及示例代码下载:(github上的项目没有作者的dubug-keystore,我这里有哦) Weixin.rar (1.46 M, 下载次数:0) pg2.6示例代码下载: XIXIWeiXin2.rar (1.34 M, 下载次数:0)

phonegap

【PhoneGap Android】如何开通 Google GCM推送服务

11. 十二月 2012
原文:http://www.pushwoosh.com/programming-push-notification/enabling-gcm-services-for-google-api/1.第一步首先到Google APIS 控制台选择你要开通的服务 2. 将 Google Cloud Messaging  这个Toggle按钮设置为ON.3. 在(终端服务页面)Terms of Service page,选择接受条款.现在你需要创建服务器的key了(Server Key)4. 按下“API Access” 按钮.5. 按下“Create new Server key”(创建一个新的服务器key). 任意一个服务器key和游览器key都能够用,服务器key这个好处是允许让你设置白名单。6. 按下 “Create”.很容易是不是?记下你的server key哦,以后推送的时候都是把消息推送到你服务器key所在的gcm服务器,由gcm服务器对用户进行推送的。另外也记下你的ProjectID ,这个可以从url上面看出,像这样: https://code.google.com/apis/console/#project:12345678912:access12345678912 就是你可以写入app客户端的ProjectID

Android, phonegap , , ,

一行代码教你如何在phoneGap Android中打开一个native的游览器(下载图片)

7. 十二月 2012
PhoneGap安卓 交流 QQ 群 250395324欢迎您的加入 参考:http://community.phonegap.com/nitobi/topics/opening_native_browser_programmatically_with_phonegap_build lz主要想在安卓中做下载,但是苦于app中window.location.href这样的方法有沙箱没法下载文件,有实验了PhoneGap API中Download的方法,发现能够下载但是没法打开,于是想到不如直接用游览器,让用户直接在游览器中下载。 代码如下: navigator.app.loadUrl('http://www.baidu.com', { openExternal:true } );

phonegap

【PhoneGap Android】Google GCM推送使用插件

28. 十一月 2012
img]原插件:https://github.com/marknutter/GCM-Cordovalz修复了几个bug如下:1.修改了将将程序放置后端时,没有通知栏弹出的问题。2.通知推送到客户端后会保存至数据库,当用户阅读到此通知后,会自动删除该记录,保证了程序放发哦后台,推送信息无法再html5 app中阅读的问题。3.当用户杀掉程序进程后仍然可以收到推送的通知栏。P.S。当用户杀掉程序进程后,推送的消息无法保存到客户端数据库。做了如上修改,仍有遗憾,希望有牛人能够继续完善。下载: GCM-Cordova-master.rar (1.57 M, 下载次数:0)

phonegap

google phonegap 讨论

28. 十一月 2012
https://groups.google.com/forum/?fromgroups#!forum/phonegap

phonegap

如何在index.html页面中添加phonegap代码

24. 十一月 2012
http://bbs.cfxixi.com/showtopic-274.aspx

phonegap

phonegap中在service中添加弹框操作

23. 十一月 2012
 PhoneGap Android 交流 QQ 群 250395324欢迎您的加入 String ns = Context.NOTIFICATION_SERVICE; /*context = cordova.getActivity().getApplicationContext();*/ mNotificationManager = (NotificationManager) context.getSystemService(ns); Notification noti = StatusNotificationIntent.buildNotification(context, "saddsa","dsasadsa", "dsaddsds"); mNotificationManager.notify(0, noti);

phonegap

phonegap三行代码得到网络类型

20. 十一月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 简化为三行代码 function checkConnectionType() { var type = navigator.network.connection.type; var _type = type; //var _online = type != Connection.NONE; return _type; }

phonegap

phonegap四行代码判断设备是否在线

20. 十一月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 简化成了四行代码 function checkConnection() { var type = navigator.network.connection.type; var _type = type; var _online = type != Connection.NONE; return _online; }

phonegap

Android百度定位插件,将原来的SDK改成PhoneGap插件供JS调用

18. 十一月 2012
下载:https://github.com/phpcxy/PhoneGap-BaiduLocPlugin PhoneGap-BaiduLocPlugin For Android Android百度定位插件,将原来的SDK改成PhoneGap插件供JS调用。 百度定位SDK请看http://dev.baidu.com/wiki/geolocation/index.php?title=AndroidAPI 使用方法 1. 拷贝JAVA文件到你的项目文件里,即/src文件夹; 2. 拷贝BaiduLoc.js文件到你的www目录,并且在html页面引入该js; 3. 拷贝libs里面的百度定位SDK(locSDK_2.3.jar与armeabi文件夹),并且在项目里面进行Build Path; 4. 打开res/xml/config.xml(Phonegap 2.0以下是plugins.xml)添加: <plugin name="BaiduLocPlugin" value="com.fulstore.plugin.BaiduLoc.BaiduLocPlugin"/> 5. 参考这里进行AndroidManifest.xml的设置: 在application标签中声明service组件 <service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" android:permission="android.permission.BAIDU_LOCATION_SERVICE"> <intent-filter> <action android:name="com.baidu.location.service_v2.3"></action> </intent-filter> </service> 声明使用权限 <permission android:name="android.permission.BAIDU_LOCATION_SERVICE"></permission> <uses-permission android:name="android.permission.BAIDU_LOCATION_SERVICE"></uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> <uses-permission android:name="android.permission.READ_LOGS"></uses-permission> 6.javascript调用方法: window.Location(success(pos),fail(err)); PS: 1. 作为一个PHP码农,我基本不懂JAVA。该插件也是不断借助google和baidu糊弄出来的,估计有BUG,希望有真正的Android开发者能完善下。 2. 本插件返回的是百度的经纬坐标与真实经纬度还是有较大差别的。只适用使用在百度地图上,但是网上也有百度坐标转换成真实经纬的方法,有需要的请自行搜索。

phonegap

phonegap拍照配置简介

12. 十一月 2012
phonegap拍照配置 navigator.camera.getPicture(toCropPage(拍摄成功后的回调函数), fail(拍摄失败后的回调函数), { quality : 50,//图片质量             targetWidth: 960,//定义图片宽度             targetHeight: 640,//图片高度 destinationType : Camera.DestinationType.FILE_URL, //拍摄完成后返回文件url sourceType : Camera.PictureSourceType.CAMERA, //采用拍照方式获取图像(可以从相册中选择) allowEdit : false,//是否允许图像编辑(iphone有效) encodingType : Camera.EncodingType.PNG //拍摄后图片的格式 });

phonegap

gcm推送最大字数

9. 十一月 2012
118个字节,每三个字节一个汉字,至多39个汉字

Android, phonegap

PhoneGap应用开发对策:如何通过苹果审核?

5. 十一月 2012
摘要:曾有人提出这样的说法,未来是iOS的,更是Android,归根结底还是PhoneGap的,由于其跨平台开发特性,PhoneGap受到诸多开发者的青睐,可在这个框架下设计的应用,在最开始接受苹果审查的时候会遇到一些麻烦,该如何是好? 最近在一些开发者社区,有人抱怨苹果拒绝自己开发的PhoneGap应用,原因是不够本地化。有开发者将之归咎于用户界面设计基于HTML。其实被拒绝也没那么难理解,苹果有一套严格的审查程序,那问题到底出在哪?又该如何处理? 首先要了解PhoneGap是什么,这是一个开源的移动开发框架,能真正实现Written once run everywhere,能基于HTML5 + CSS + JavaScript等标准协议开发跨平台的移动应用。亮点是对智能手机本地功能的调用,如摄像头、地理位置、加速度传感器、文件管理、联系人、指南针、推送、存储等。它能够包装你的HTML体验使之更加本地化,还能把应用打包发布到App Store中。听起来是挺不错,但开发就不是件容易的事,即使开发出来了,审核也是个问题。 无法通过审核的原因 首先,苹果不会因为App使用的用户界面基于HTML而去拒绝一个应用。实际上,许多苹果自己的App或者广告平台都基于HTML、CSS和JavaScript,比如Apple Store里的iAd广告平台就是其中一个。除了苹果自己的应用外,还有LinkedIn,Wikipedia等等。当然,当中不是所有App都用PhoneGap,但其用户界面都基于HTML,所以就排除HTML为罪魁祸首这一说。 1)苹果会拒绝没有如下特征的应用: 能给用户带来应用体验而非网页体验; 在iOS生态系统中,使用起来轻松自如; 能与移动端Web体验区分开。 这几条对所有应用都适用,并不仅仅是UI基于HTML开发的应用,如果不知道苹果的具体审核标准是什么,记住一点就可以,所有标准都和用户体验有关。在苹果的“iOS User Interface Guidelines”(iOS UI设计说明)中就有大量关于哪些能通过哪些无法通过的详细信息,在UI guidelines中,苹果尤其强调“基于Web的设计”这个部分: 如果应用是基于Web的设计,建议三思而行 如果基于Web,建议你确保App带给用户的是iOS应用体验,而不是Web体验。记住,用户可以通过Safari访问你的网站。 图片来源:lukor 在利用HTML及与之相关的技术开发应用时,一定要仔细阅读完整的“iOS User Interface Guidelines”。除了iOS User Interface Guidelines,苹果的“App Store Review Guidelines”对于如何通过App审查也有相关说明,当中就有许多有关基于HTML开发的App可能被拒绝的情况,值得注意的是如下几条: 2.12:不是特别有用的,独特的应用,只是简单与网站捆绑后作为App的应用,无法提供持续娱乐价值的应用可能会被拒绝。 10.3:没有正确使用系统所提供的选项(如按钮,图标)的应用以及与Apple iOS Human Interface Guidelines的规定描述不符的应用可能会被拒绝。 12.3:只是简单的web剪接,内容聚合或者链接搜集,这类应用可能会被拒绝。 2)出现这些迹象,应用也有可能被拒绝 如果你的应用只是PhoneGap包装下的网站,可能被拒绝。当然也有例外,但千万不要抱侥幸心理。 如果你的应用需要用户缩放页面才能查阅内容,就有可能被拒绝。应用要有应用的感觉,要给人直观性,不要让用户去找功能,不要让用户返回上一个导航去寻找有用的东西。 如果应用看上去就像加了超链接的文版,没有本地化风格,也会被拒绝。 注意你应用的反应时间,这些时间与用户操作处理时间以及与对服务器的反应时间都相关,苹果不喜欢慢的应用或者没有反应的App。同样,如果用户按了某个按钮,结果等了好几秒钟才有反应,也有可能被拒绝。 苹果的判断界限很多时候很模糊,尤其是对那些已经经过评估的独立开发者开发的应用更是如此。他们对每个应用都会进行优点,功能,用户体验评估。另外iOS User Interface Guidelines以及App Store Review Guidelines是动态文件,可能会随操作系统的变化而变化,或根据新的应用设计带来的新问题而改变,所以要记住定期阅读这些文件。 被拒绝了该怎么做? 前面提到PhoneGap有这么多功能,但它无法使你免于应用审核。由于PhoneGap的用户界面是基于web技术,不太可能立即被接受。在开发过程中,设计师或开发者一定要注意,UI/UX设计一定要与操作平台或生态系统的要求相符。当你在设计或开发一个PhoneGap应用的时候,要思考的是如何让应用看上去更加本地化,如何更有移动应用的感觉而不是网页感觉,核心问题就是“用户体验”是否好。 1)做应用的时候,要思考下面几个问题: 应用的UI界面范例与平台相符吗? 应用给人的视觉效果如何? 应用看上去像一个网页吗?界面有利于触屏操作吗? 下面以Fresh Food Finder(App Store里的PhoneGap应用)为例,我们将检验两个视觉效果。下面两张图是同一个应用,均由PhoneGap模拟器捕捉。 图片来源:Adobe 左图有HTML内容但没有CSS风格,如果比较视觉效果,左边界面看起来就像网页,而右边的看上去则非常有应用的感觉,顶部有导航元素,通过右边箭头还能继续查看下一级内容,每一条都有UI元素,促使用户继续浏览内容。 图片来源:Adobe 看上图,有什么问题呢? 首先,上图是一个经过包装的网站,苹果讨厌的就是把网站用本地化的外壳伪装起来,这个外壳是空的,没有任何逻辑可言,如果掉线,伪装就会失败,与移动端的Web体验没什么差别,也没有增加任何价值。 其二,用户界面没有优化移动体验,用户需要缩放等操作来阅读内容,并且,导航不仅与内容没有明确的区分开,也不利于触屏操作。开发应用的时候,要让用户操作起来有熟悉的感觉,当然,你无须模仿每个本地应用的UI风格,但用户互动范例应该相似,UI要够直观,清爽。想要理解什么叫“有App感觉的应用”,就多看看其它公诸于众的应用或者已经通过苹果审核的设计。 现在国外有许多比较应用UI的网站,进去看看对UI设计会有很大帮助。例如: Mobile partners Inspired UI Pttrns 许多工具及框架都能帮助你将HTML体验打造得更加本地化,点击链接可进入下载。例如: Twitter Bootstrap iUI jQuery Mobile Sencha Touch Kendo UI app-UI Zurb Foundation Moobile Chocolate Chip UI Dojo Toolkit jqMobi 图片来源:Mobile-Patterns 2)交互性与性能注意事项 (1)优化视觉脱节环节:如果用户必须通过HTML链接来下载一个单独的页面,会带来视觉上的脱节,在这个环节需要进行优化。苹果貌似更青睐那些给人感觉比较统一的应用,能为用户带来无缝体验的应用。在异步下载数据或者动态更新内容的时候可以采用单页面架构,总的来说就是要让页面之间的衔接更快更不容易让人察觉。 (2)优化数据获取过程:你还需要在应用内及应用外优化数据获取过程。数据交换尽量最小化,只有在需要的时候才传输数据,最好采用轻量级数据交换格式,这样就会缓解数据传输延迟现象,让你的应用变得更快。 (3)应用内操作过渡要连贯:尤其要注意这条,如果你的应用出现闪烁,不连贯的情况,那你就需要重新访问应用,试试自己是如何在屏幕上移动应用内的按钮或者设置的。去查找一些技术,让GPU为只是使用CSS的HTML DOM元素服务,许多情况下,这些技术能改变整个应用性能。 (4)进度条与旋转指针:另外在处理一些需要时间的事情时,比如与服务器交换数据,可以增加一些简单标志,比如进度条或者旋转指针,可以让你的应用感觉更快更加本地化。 总的来讲,注意你的用户体验设计,苹果的审核程序也并非一成不变的,还得看你是什么应用。千万不要认为用了PhoneGap跨平台开发工具就能免除审核这一步。苹果相当注重应用的品质及用户体验,如果表现不好,出现漏洞,或者审美达不到他们标准,也会被拒绝,苹果对自己的生态系统要求比较高,但记住一点,越注重用户体验,通过的几率就越高。 (文章翻译:陈徐天九  责任编辑:杨依帆) 文章来源:Adobe

phonegap

发现一个可以下phonegap源码的地方哈

2. 十一月 2012
发现一个可以下phonegap源码的地方哈(用svn)https://svn.apache.org/repos/asf/incubator/callback/github安卓phonegap下载:https://github.com/apache/cordova-android ios phonegap源码下载:https://github.com/apache/cordova-ios

phonegap

phonegap实现照片上传

14. 十月 2012
因为业务的需要,需要实现一个照片上传的功能,但因为是html5开发的app,不希望用原生态的方法,恰好phonegap中有上传的文件的接口,于是乎试验了一下,经过一天的奋战,总算还是涉险过关了,说一下思路吧,首先是拍照的功能,拍照这个依托phonegap的api实现起来并不难,拍摄好的照片是一个文件路径,在代码中将这个路径传给上传接口,这个接口是post类型的,这些都是phonegap封装好的,然后是服务器那边的接收,lz是。net流派的,采用的是wcf的接收方式,由于发送的是文件流,获得文件流之后使用File的SavaAs方法,将文件存到文件夹这边完成,可喜可贺呵呵  附代码: var options = new FileUploadOptions(); options.fileKey = "fileAddPic";//用于设置参数,对应form表单里控件的name属性,这是关键,废了一天时间,完全是因为这里,这里的参数名字,和表单提交的form对应 var imagefilename = Number(new Date())+".jpg"; options.fileName=imagefilename; //options.fileName = imageURI.substr(imageURI.lastIndexOf('/') + 1); //如果是图片格式,就用image/jpeg,其他文件格式上官网查API options.mimeType = "image/jpeg"; options.mimeType = "multipart/form-data";//这两个参数修改了,后台就跟普通表单页面post上传一样 enctype="multipart/form-data" //这里的uri根据自己的需求设定,是一个接收上传图片的地址 var uri = encodeURI("http://server.com/aaa.svc/PostWithImageData/"); //alert(imageURI); //alert(uri); options.chunkedMode = false; var params = new Object(); params.fileAddPic = imageURI; options.params = params; var ft = new FileTransfer(); ft.upload(imageURI, uri, win, fail, options);   服务器端代码: HttpPostedFile file = HttpContext.Current.Request.Files["fileAddPic"]; try { string targetFilePath = "D:\\aaa\\" + file.FileName; file.SaveAs(targetFilePath); } catch (Exception e) { log.Error(">>>发生错误,详情如下:"); log.Error(e); } return file.FileName; 参考:https://groups.google.com/forum/?fromgroups=#!topic/phonegap/3XhwEyPnw6I

phonegap

关于安卓Web App如何实现推送(使用谷歌gcm)

6. 十月 2012
在上次按照pushwoosh文档配置了google gcm服务之后,一直没能试验成功他们的sdk,今天终于成功收到推送了,第一次失败的时候是因为lz的联想机是阉割机,没法获得google的推送token以告吹,这次换了台三星的机器,马上就拿到了推送token方便lz能够放心大胆的走下步了。拿到token之后lz不想用pushwoosh的服务器端推送服务,因为一旦要批量的去推,必须调用它们公司的remote api,而它们公司的api是要收费的,一个月要20欧元。 hehe,决定自己去搞服务器端,以".net andriod" 推送为关键字发现codeProject有相关的文章前去一试(记得把乱码文字改对register)(http://www.codeproject.com/Tips/434338/Android-GCM-Push-Notification)确实好用,手机也收到了推送,唯一的问题是客户端因为做的是webapp必须有一个phonegap插件,而插件里面有些重要的组件封装在了jar包里,没撤面对了这种窘境噶,所以现在要么买pushwoosh要么找到其他的phonegap组件。  经过一天努力终于另外找了一个插件,这个插件可以监听gcm传送过来的消息。(https://github.com/marknutter/GCM-Cordova) 美中不足的是安卓消息框需要自己写(https://github.com/phonegap/phonegap-plugins/tree/master/Android/StatusBarNotification) 为了解决上面的两个问题,lz尝试写了Demo:http://bbs.cfxixi.com/showtopic-298.aspx     .net推送代码可以用台湾同胞的(能够传输中文)(http://www.dotblogs.com.tw/shadow/archive/2012/07/05/73236.aspx) 完美解决了,如果有疑问可以来我phonegap交流群250395324交流

admob, phonegap

phonegap安卓禁止返回键作用

29. 九月 2012
  利用pg可以监听返回键事件只要在事件中别定义代吗就能做到禁止返回的操作。 document.addEventListener("backbutton", eventBackButton, false); //返回键 function eventBackButton() { /*navigator.notification.confirm('确定退出App?', // 显示信息 onConfirm, // 按下按钮后触发的回调函数,返回按下按钮的索引 '退出应用', // 标题 '确定,取消' // 按钮标签 );*/ }

phonegap

解决phonegap 出现window.plugin.undefined 模拟器上正常,真机报错问题

18. 九月 2012
解决phonegap 出现window.plugin.undefined 模拟器上正常,真机报错问题

phonegap

如何通过phonegap安卓插件实现复制(到剪贴板)黏贴操作

14. 九月 2012
今天试验了phonegap的复制(到剪贴板)黏贴操作发现还蛮好用 下面说说怎么用: lz按照教程写了一个demo,大家可以去我qq群共享下载:250395324(自己写死了一段文字然后复制到剪贴板最后输出显示弹出屏幕) 1.首先我们可以去github下载插件 https://github.com/phonegap/phonegap-plugins/tree/master/Android/ClipboardManager   2.然后按照说明的教程一步一步去配置:   The plugin creates the object window.plugins.clipboardManager with the methods copy(str, success, fail) that copies the given string paste(success, fail) that returns the text from the Clipboard success and fail are callback functions. An example for copy: window.plugins.clipboardManager.copy( "the text to copy", function(r){alert("copy is successful")}, function(e){alert(e)} ); An example for paste: window.plugins.clipboardManager.paste( function(r){alert("The text in the clipboard is " + r)}, function(e){alert(e)} ); demo大家可以去我qq群共享下载:250395324

phonegap

phonegap检查网络状态

7. 九月 2012
function checkConnection() { var networkState = navigator.network.connection.type; var states = {}; states[Connection.UNKNOWN] = 1; states[Connection.ETHERNET] = 2; states[Connection.WIFI] = 3; states[Connection.CELL_2G] = 4; states[Connection.CELL_3G] = 5; states[Connection.CELL_4G] = 6; states[Connection.NONE] = 0; if(states[networkState]==0) { return false; } else { return true; } }

phonegap

phonegap安卓如何让app进行缩放

28. 八月 2012
如何让app进行缩放 this.appView.getSettings().setBuiltInZoomControls(true)

phonegap

安卓开发环境DDMS 20Phonegap使用教程(适用eclipse,motodev除外)

8. 八月 2012
This guide describes how to set up your development environment for Cordova and run a sample application. Note that Cordova used to be called PhoneGap, so some of the sites still use the old PhoneGap name. 1. Requirements  Eclipse 3.4+ 2. Install SDK + Cordova  Download and install Eclipse Classic Download and install Android SDK Download and install ADT Plugin Download the latest copy of Cordova and extract its contents. We will be working with the Android directory. 3. Setup New Project  Launch Eclipse, and select menu item New Project  Then specify new application project  Then speciy an Application Name, a Project Name and Package Name with Namespace  Then select a graphic  Then Create a Blank Activity  Make sure the activity doesn't inherit from anything. You most likely won't have PhoneGap on your Eclipse Workspace. Once this is done, click finish In the root directory of your project, create two new directories: /libs assets/www Copy cordova-2.0.0.js from your Cordova download earlier to assets/www Copy cordova-2.0.0.jar from your Cordova download earlier to /libs Copy xml folder from your Cordova download earlier to /res Verify that cordova-2.0.0.jar is listed in the Build Path for your project. Right click on the /libs folder and go to Build Paths/ > Configure Build Path.... Then, in the Libraries tab, add cordova-2.0.0.jar to the project. If Eclipse is being temperamental, you might need to refresh (F5) the project once again. Edit your project's main Java file found in the src folder in Eclipse: Add import org.apache.cordova.*; Change the class's extend from Activity to DroidGap Replace the setContentView() line with super.loadUrl("file:///android_asset/www/index.html"); Right click on AndroidManifest.xml and select Open With > Text Editor Paste the following permissions between the <uses-sdk.../> and <application.../> tags. <supports-screens     android:largeScreens="true"     android:normalScreens="true"     android:smallScreens="true"     android:resizeable="true"     android:anyDensity="true"/><uses-permissionandroid:name="android.permission.VIBRATE"/><uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/><uses-permissionandroid:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/><uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/><uses-permissionandroid:name="android.permission.INTERNET"/><uses-permissionandroid:name="android.permission.RECEIVE_SMS"/><uses-permissionandroid:name="android.permission.RECORD_AUDIO"/><uses-permissionandroid:name="android.permission.MODIFY_AUDIO_SETTINGS"/><uses-permissionandroid:name="android.permission.READ_CONTACTS"/><uses-permissionandroid:name="android.permission.WRITE_CONTACTS"/><uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permissionandroid:name="android.permission.GET_ACCOUNTS"/><uses-permissionandroid:name="android.permission.BROADCAST_STICKY"/> *Note You are adding a blanket list of permissions to your application. You should remove permissions you aren't using before submitting your application to Google Play. Support orientation changes by pasting the following inside the <activity> tag.   android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" Your AndroidManifest.xml file should look like   4. Hello World  Create and open a new file named index.html in the assets/www directory. Paste the following code: <!DOCTYPE HTML><html><head><title>Cordova</title><scripttype="text/javascript"charset="utf-8"src="cordova-2.0.0.js"></script></head><body><h1>Hello World</h1></body></html> 5A. Deploy to Simulator  Right click the project and go to Run As > Android Application Eclipse will ask you to select an appropriate AVD. If there isn't one, then you'll need to create it. 5B. Deploy to Device  Make sure USB debugging is enabled on your device and plug it into your system. (Settings > Applications > Development) Right click the project and go to Run As > Android Application Done! 

phonegap

使用Phonegap(Cordova)插件将数据存贮到Native设备的Sqlite数据库中

3. 八月 2012
今天上海貌似有台风,一会傍晚在18楼看风雨一定很壮观。好了,今天阿赛介绍了我一个phonegap的插件是讲如果利用phonegap进行native sqlite存储的,自己试验了一下发现确实还挺好用的,为了向阿赛学习我也出一篇文章讲讲怎么用吧~~ 项目源代码可去我的qq群共享下载:250395324 插件下载:https://github.com/ApplicationCraft/PGSQLitePlugin  阿赛博客文章:http://sailei1.iteye.com/blog/1617214 ok,我们开始第一步是引入.java文件 请导入PGSQLitePlugin.java文件 第二步: 请导入pgsqliteplugin.js文件 第三步添加plugin.xml 在plugin.xml中添加tag(记得修改成自己的package 名字哦)   [html] view plaincopy   <plugin name="PGSQLitePlugin" value="com.tricedesigns.PGSQLitePlugin"/>   第四步添加调用函数,这里我在载入的时候执行一个建表插入查询的操作     [javascript] view plaincopy   var db = new PGSQLitePlugin("testdb.sqlite3", function(dbResult, dbObject){           console.log("Database status=" + dbResult.status);           console.log("Database version=" + dbResult.version);           db = dbObject;           db.open(function(){               //alert("chengong")                              db.executeSql("CREATE TABLE IF NOT EXISTS test (testID TEXT NOT NULL PRIMARY KEY, fio TEXT NOT NULL, adress TEXT)", function(){                   //console.log( "table test was created" );                   //alert("table test was created");                                                       db.insert("test", {testID:11,fio : "101s", adress : "Username11" }, function(id){                        //console.log("id="+id);                       //alert("sadsdsa");                       alert("id="+id);                                                                                                                                                            }, function(er){                       console.log("error="+er);                   });                                                          db.query("test", ["testID", "fio"], null, [], null, null, "testID", null, function(res){                            for (var i in res.rows){                                for (var key in res.rows[i]){                                    alert(key + "=" + res.rows[i][key]);                                   //console.log(key + "=" + res.rows[i][key] );                                }                            }                        }, function(er){                           console.log("error="+er);                       });                                                                                                                 }, function(err){                   console.log("error creating table test::" + err);               });                                                        }, function(err){               alert("Error create database::err=" + err);               //console.log("Error create database::err=" + err);           });                                                       //navigator.notification.alert("Database status=" + dbResult.status+"Database version=" + dbResult.version);       }, function(err){           console.log("Error create database::err=" + err);       });   如此执行后便可弹出你数据库中插入的字段和字段值   项目源代码可去我的qq群共享下载:250395324

phonegap

利用phonegap实现本地数据库

2. 八月 2012
SQLite database on PhoneGap 据说安卓和ios的native的数据库执行速度是websql的5倍,并且websql将停止更新并被indexdb取代。 依据:http://stackoverflow.com/questions/6956075/html5-sqlite-vs-native-database-in-case-of-phonegap 原文:http://stackoverflow.com/questions/5139302/sqlite-database-on-phonegap 插件下载:https://github.com/davibe/Phonegap-SQLitePlugin(只是ios的插件,安卓屌丝怎么办。。。。。。) 最后在阿赛同学的推荐下得到了一个安卓ios都有的插件:https://github.com/ApplicationCraft/PGSQLitePlugin

phonegap

Phonegap安卓如何通过点击Contact获得联系人列表的名字和电话

31. 七月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 项目源代码可去我的qq群共享下载 1.引入ContactView.java package com.tricedesigns; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; public class ContactView extends Plugin { private static final int PICK_CONTACT = 1; private String callback; @Override public PluginResult execute(String action, JSONArray args, String callbackId) { startContactActivity(); PluginResult mPlugin = new PluginResult(PluginResult.Status.NO_RESULT); mPlugin.setKeepCallback(true); this.callback = callbackId; return mPlugin; } public void startContactActivity() { Intent intent = new Intent(Intent.ACTION_PICK); intent.setType(ContactsContract.Contacts.CONTENT_TYPE); this.ctx.startActivityForResult((Plugin) this, intent, PICK_CONTACT); } @Override public void onActivityResult(int reqCode, int resultCode, Intent data) { String name = null; String number = null; String email = null; switch (reqCode) { case (PICK_CONTACT): if (resultCode == Activity.RESULT_OK) { Uri contactData = data.getData(); Cursor c = this.ctx.managedQuery(contactData, null, null, null, null); if (c.moveToFirst()) { String ContactID = c.getString(c .getColumnIndex(ContactsContract.Contacts._ID)); String hasPhone = c.getString(c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); if (Integer.parseInt(hasPhone) == 1) { Cursor phoneCursor = this.ctx.managedQuery( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "='" + ContactID + "'", null, null); while (phoneCursor.moveToNext()) { number = phoneCursor .getString(phoneCursor .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); } } // get email address Cursor emailCur = this.ctx.managedQuery( ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + "='" + ContactID + "'", null,null); while (emailCur.moveToNext()) { // This would allow you get several email addresses // if the email addresses were stored in an array email = emailCur.getString( emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)); //String emailType = emailCur.getString( // emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE)); } emailCur.close(); name = c.getString(c.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME)); JSONObject contactObject = new JSONObject(); try { contactObject.put("name", name); contactObject.put("phone", number); contactObject.put("email", email); } catch (JSONException e) { e.printStackTrace(); } this.success(new PluginResult(PluginResult.Status.OK, contactObject), this.callback); } } break; } } } 2.引入ContactView.js文件 var ContactView = function() {}; ContactView.prototype.show = function(successCallback, failCallback) { function success(args) { successCallback(args); } function fail(args) { failCallback(args); } return cordova.exec(function(args) { success(args); }, function(args) { fail(args); }, 'ContactView', '', []); }; cordova.addConstructor(function() { cordova.addPlugin('contactView', new ContactView()); PluginManager.addService("ContactView","com.tricedesigns.ContactView"); }); 3.修改plugins.xml 添加标签: <plugin name="ContactView" value="com.tricedesigns.ContactView"/> 4.调用方法: function ccc(){ window.plugins.contactView.show( function(contact) { alert(contact.name+"|"+contact.phone); //document.getElementById("contact-name-from-native").value = contact.name; //document.getElementById("contact-phone").value = contact.phone; }, function(fail) { alert("We were unable to get the contact you selected."); } ); } 5.大功告成~~ 如图:   点击彩铃后:

andriod, phonegap

phonegap(安卓)如何实现微博认证登录

26. 七月 2012
项目可去我的qq群共享下载:250395324 今天看到了一篇讲这么拿腾讯微博accesstoken的文章,发现其实新浪weibo登录的问题已经破解了,得到token之后直接调用api文档中的方法就能调用新浪微博各个api中的方法了。 至于怎么做,首先请看一篇文章:http://blog.csdn.net/xiaoguang44/article/details/7784138 大致思路就是上面文章那样,但是我们做的是新浪微博,所以做个小小的修改,不用通过服务器端。   1.在第一次请求打开子游览器框的时候传入新浪微博的路由(参考http://open.weibo.com/wiki/Oauth2) 在第一个连接的代码基础上我们修改一下:  cb.showWebPage("https://api.weibo.com/oauth2/authorize?client_id=3431190291&display=mobile&response_type=token&redirect_uri=http://xixiwei.sinaapp.com"); 如果不知道clientId怎么获得看这篇文章:http://blog.csdn.net/xiaoguang44/article/details/7753096   2.通过url变更时候的参数变化获取token(获取自己截取了) cb.onLocationChange = function (loc){ alert(loc) }; 代码示例:  cb = window.plugins.childBrowser; cb.showWebPage("https://api.weibo.com/oauth2/authorize?client_id=3431190291&display=mobile&response_type=token&redirect_uri=http://xixiwei.sinaapp.com"); cb.onLocationChange = function (loc){ alert(loc); var locStart=loc.indexOf("access_token="); var locEnd=loc.indexOf("&remind_in"); var sinaToken=""; if(locStart>0&&locEnd>0) { sinaToken=loc.substring(locStart+13,locEnd); alert(sinaToken); cb.close(); } };   3.利用这个token我们便可以调用文档中的各个api了 http://open.weibo.com/wiki/API%E6%96%87%E6%A1%A3_V2 于是乎这个微博验证登录通过获取获得的token完破解了。 p.s如果希望将子游览器的url条去掉可以设置(在childBrowser.java中) private boolean showLocationBar = false;

phonegap

phonegap(安卓)调用蓝牙及检测蓝牙功能

24. 七月 2012
  Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入   项目代码可去我的qq群共享下载250395324 phonegap蓝牙功能可以让我们判断蓝牙的开启状态、打开、关闭、匹配设备等 我们要做的只是加入.java和.js文件(插件包了提供了一个index.html的页面) 记得添加xml <plugin name="BluetoothPlugin" value="com.tricedesigns.BluetoothPlugin"/> 效果如下:

andriod, phonegap

Phonegap(安卓)实现扫描验证码及生成验证码的功能

23. 七月 2012
项目源代码可去我的qq群共享下载:250395324 插件可以去这里下载:https://github.com/phonegap/phonegap-plugins/ BarcodeScanner 1.添加一个项目,路径:File -> New Android Project -> create project,添加一个已有的项目。 Add the 'LibraryProject' into Eclipse. File -> New Android Project -> create project from existing source. 2.In the new project you've just added to Eclipse go to the project properties. Select the Android section and at the bottom of the dialog check the "Is Library" checkbox. 3.In your application go into the project properties. In the Android section under library click the Add button and select the library you created in step 2. 4.To install the plugin, move barcodescanner.js to your project's www folder and include a reference to it in your html files. 5.添加BarcodeScanner.java文件 6.<plugin name="BarcodeScanner" value="com.phonegap.plugins.barcodescanner.BarcodeScanner"/> 记得改你的package名字。 7.Add the following activity to your AndroidManifest.xml file. It should be added inside the &lt;application/&gt; tag.   Add the following activity to your AndroidManifest.xml file. It should be added inside the <application/> tag. <!-- ZXing activities --> <activity android:name="com.google.zxing.client.android.CaptureActivity" android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden"> <intent-filter> <action android:name="com.phonegap.plugins.barcodescanner.SCAN"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name="com.google.zxing.client.android.encode.EncodeActivity" android:label="@string/share_name"> <intent-filter> <action android:name="com.phonegap.plugins.barcodescanner.ENCODE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>`       9. And make sure you have the following permission in your AndroidManifest.xml file:     <uses-permission android:name="android.permission.CAMERA" />     10.调用方法:scan   window.plugins.barcodeScanner.scan( function(result) { alert("We got a barcode\n" + "Result: " + result.text + "\n" + "Format: " + result.format + "\n" + "Cancelled: " + result.cancelled); }, function(error) { alert("Scanning failed: " + error); } );     encode方法:   window.plugins.barcodeScanner.encode(BarcodeScanner.Encode.TEXT_TYPE, "http://www.nytimes.com", function(success) { alert("encode success: " + success); }, function(fail) { alert("encoding failed: " + fail); } );  

andriod, phonegap

Andriod Phonegap实现系统推送

19. 七月 2012
  Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入  (最近突然发现gcm是可以像ios那样做推送的,哦也,但是仍然需要找一个phonegap插件才行)  花了不少的时间研究phonegap,希望能够找出一个能够取代轮询的推送方式,尝试了pushwoosh之后发现有两个问题,一个是手机必须集成有andriod apis的组件,其次不知道是不是什么原因总是有account失败的错误,实在是没有办法,想想即便解决了,第一个问题依然难以解决。 后来在江哥的推荐下找到了一个系统推送的phonegap插件 好了,首先我们来介绍一下用法: 1.(两个最关键的文件(.java和.js组件))   /* * * Copyright (C) 2011 Dmitry Savchenko <dg.freak@gmail.com> * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ package com.tricedesigns; import org.apache.cordova.api.PluginResult.Status; import org.json.JSONArray; import org.json.JSONException; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.util.Log; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; public class StatusBarNotification extends Plugin { // Action to execute public static final String NOTIFY = "notify"; public static final String CLEAR = "clear"; /** * Executes the request and returns PluginResult * * @param action Action to execute * @param data JSONArray of arguments to the plugin * @param callbackId The callback id used when calling back into JavaScript * * @return A PluginRequest object with a status * */ @Override public PluginResult execute(String action, JSONArray data, String callbackId) { String ns = Context.NOTIFICATION_SERVICE; mNotificationManager = (NotificationManager) ctx.getSystemService(ns); context = ctx.getApplicationContext(); PluginResult result = null; if (NOTIFY.equals(action)) { try { String title = data.getString(0); String body = data.getString(1); Log.d("NotificationPlugin", "Notification: " + title + ", " + body); showNotification(title, body); result = new PluginResult(Status.OK); } catch (JSONException jsonEx) { Log.d("NotificationPlugin", "Got JSON Exception " + jsonEx.getMessage()); result = new PluginResult(Status.JSON_EXCEPTION); } } else if (CLEAR.equals(action)){ clearNotification(); } else { result = new PluginResult(Status.INVALID_ACTION); Log.d("NotificationPlugin", "Invalid action : "+action+" passed"); } return result; } /** * Displays status bar notification * * @param contentTitle Notification title * @param contentText Notification text * */ public void showNotification( CharSequence contentTitle, CharSequence contentText ) { int icon = R.drawable.nofication; long when = System.currentTimeMillis(); Notification notification = new Notification(icon, contentTitle, when); //notification.flags |= Notification.FLAG_NO_CLEAR; //Notification cannot be clearned by user Intent notificationIntent = new Intent((Context) ctx, ctx.getClass()); PendingIntent contentIntent = PendingIntent.getActivity((Context) ctx, 0, notificationIntent, 0); notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); mNotificationManager.notify(1, notification); } /** * Removes the Notification from status bar */ public void clearNotification() { mNotificationManager.cancelAll(); } private NotificationManager mNotificationManager; private Context context; }       .js文件   /* * * Copyright (C) 2011 Dmitry Savchenko <dg.freak@gmail.com> * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ /** * * Constructor */ var NotificationMessenger = { notify:function(title, body) { return cordova.exec(null, null, 'StatusBarNotification','notify', [title, body]); }, clear:function() { return cordova.exec(null, null, 'StatusBarNotification', 'clear', []); }, } /** * @param title Title of the notification * @param body Body of the notification */ /*NotificationMessenger.prototype.notify = function(title, body) { return cordova.exec(null, null, 'StatusBarNotification', 'notify', [title, body]); }; *//** * Clears the Notificaiton Bar *//* NotificationMessenger.prototype.clear = function() { return cordova.exec(null, null, 'StatusBarNotification', 'clear', []); }; *//** * Load StatusBarNotification * *//* cordova.addConstructor(function() { cordova.addPlugin('statusBarNotification', new NotificationMessenger()); // @deprecated: No longer needed in PhoneGap 1.0. Uncomment the addService code for earlier // PhoneGap releases. // PluginManager.addService("StatusBarNotificationPlugin","com.trial.phonegap.plugin.directorylisting.StatusBarNotificationPlugin"); });*/     3.记得放入一张notification.png的图片在res下的drawable文件夹下   4.修改plugin.xml   <plugin name="StatusBarNotification" value="com.tricedesigns.StatusBarNotification"/>     5.定义调用的函数   function aaa(){ NotificationMessenger.notify("CF西西", "Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入"); }   6.ok,运行一下你会看到如下效果(项目代码可去我的qq群共享下载哦250395324)  

phonegap

Phonegap中如何打开一个子游览器窗口

18. 七月 2012
  Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入   今天上海早晨下了场雨,天气很舒服,话说今天的探索有hit,不但知道了st中的localstorage方法可以存储个字段,而且还发现了phonegap中可以打开一个子窗口,为日后的微博登录打下了基础。 好了下面来介绍一下这一phonegap插件: 1.首先引入.java文件   /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2011, Nitobi Software Inc. * Copyright (c) 2010-2011, IBM Corporation */ package com.tricedesigns; import java.io.IOException; import java.io.InputStream; import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.R; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.text.InputType; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.RelativeLayout; public class ChildBrowser extends Plugin { protected static final String LOG_TAG = "ChildBrowser"; private static int CLOSE_EVENT = 0; private static int LOCATION_CHANGED_EVENT = 1; private String browserCallbackId = null; private Dialog dialog; private WebView webview; private EditText edittext; private boolean showLocationBar = true; /** * Executes the request and returns PluginResult. * * @param action The action to execute. * @param args JSONArry of arguments for the plugin. * @param callbackId The callback id used when calling back into JavaScript. * @return A PluginResult object with a status and message. */ public PluginResult execute(String action, JSONArray args, String callbackId) { PluginResult.Status status = PluginResult.Status.OK; String result = ""; try { if (action.equals("showWebPage")) { this.browserCallbackId = callbackId; // If the ChildBrowser is already open then throw an error if (dialog != null && dialog.isShowing()) { return new PluginResult(PluginResult.Status.ERROR, "ChildBrowser is already open"); } result = this.showWebPage(args.getString(0), args.optJSONObject(1)); if (result.length() > 0) { status = PluginResult.Status.ERROR; return new PluginResult(status, result); } else { PluginResult pluginResult = new PluginResult(status, result); pluginResult.setKeepCallback(true); return pluginResult; } } else if (action.equals("close")) { closeDialog(); JSONObject obj = new JSONObject(); obj.put("type", CLOSE_EVENT); PluginResult pluginResult = new PluginResult(status, obj); pluginResult.setKeepCallback(false); return pluginResult; } else if (action.equals("openExternal")) { result = this.openExternal(args.getString(0), args.optBoolean(1)); if (result.length() > 0) { status = PluginResult.Status.ERROR; } } else { status = PluginResult.Status.INVALID_ACTION; } return new PluginResult(status, result); } catch (JSONException e) { return new PluginResult(PluginResult.Status.JSON_EXCEPTION); } } /** * Display a new browser with the specified URL. * * @param url The url to load. * @param usePhoneGap Load url in PhoneGap webview * @return "" if ok, or error message. */ public String openExternal(String url, boolean usePhoneGap) { try { Intent intent = null; if (usePhoneGap) { intent = new Intent().setClass(this.ctx.getContext(), org.apache.cordova.DroidGap.class); intent.setData(Uri.parse(url)); // This line will be removed in future. intent.putExtra("url", url); // Timeout parameter: 60 sec max - May be less if http device timeout is less. intent.putExtra("loadUrlTimeoutValue", 60000); // These parameters can be configured if you want to show the loading dialog intent.putExtra("loadingDialog", "Wait,Loading web page..."); // show loading dialog intent.putExtra("hideLoadingDialogOnPageLoad", true); // hide it once page has completely loaded } else { intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); } this.ctx.startActivity(intent); return ""; } catch (android.content.ActivityNotFoundException e) { Log.d(LOG_TAG, "ChildBrowser: Error loading url "+url+":"+ e.toString()); return e.toString(); } } /** * Closes the dialog */ private void closeDialog() { if (dialog != null) { dialog.dismiss(); } } /** * Checks to see if it is possible to go back one page in history, then does so. */ private void goBack() { if (this.webview.canGoBack()) { this.webview.goBack(); } } /** * Checks to see if it is possible to go forward one page in history, then does so. */ private void goForward() { if (this.webview.canGoForward()) { this.webview.goForward(); } } /** * Navigate to the new page * * @param url to load */ private void navigate(String url) { InputMethodManager imm = (InputMethodManager)this.ctx.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0); if (!url.startsWith("http") && !url.startsWith("file:")) { this.webview.loadUrl("http://" + url); } else { this.webview.loadUrl(url); } this.webview.requestFocus(); } /** * Should we show the location bar? * * @return boolean */ private boolean getShowLocationBar() { return this.showLocationBar; } /** * Display a new browser with the specified URL. * * @param url The url to load. * @param jsonObject */ public String showWebPage(final String url, JSONObject options) { // Determine if we should hide the location bar. if (options != null) { showLocationBar = options.optBoolean("showLocationBar", true); } // Create dialog in new thread Runnable runnable = new Runnable() { /** * Convert our DIP units to Pixels * * @return int */ private int dpToPixels(int dipValue) { int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, (float) dipValue, ctx.getContext().getResources().getDisplayMetrics() ); return value; } public void run() { // Let's create the main dialog dialog = new Dialog(ctx.getContext(), android.R.style.Theme_NoTitleBar); dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog; dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setCancelable(true); dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { public void onDismiss(DialogInterface dialog) { try { JSONObject obj = new JSONObject(); obj.put("type", CLOSE_EVENT); sendUpdate(obj, false); } catch (JSONException e) { Log.d(LOG_TAG, "Should never happen"); } } }); // Main container layout LinearLayout main = new LinearLayout(ctx.getContext()); main.setOrientation(LinearLayout.VERTICAL); // Toolbar layout RelativeLayout toolbar = new RelativeLayout(ctx.getContext()); toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, this.dpToPixels(44))); toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2)); toolbar.setHorizontalGravity(Gravity.LEFT); toolbar.setVerticalGravity(Gravity.TOP); // Action Button Container layout RelativeLayout actionButtonContainer = new RelativeLayout(ctx.getContext()); actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); actionButtonContainer.setHorizontalGravity(Gravity.LEFT); actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL); actionButtonContainer.setId(1); // Back button ImageButton back = new ImageButton(ctx.getContext()); RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT); back.setLayoutParams(backLayoutParams); back.setContentDescription("Back Button"); back.setId(2); try { back.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_left.png")); } catch (IOException e) { Log.e(LOG_TAG, e.getMessage(), e); } back.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { goBack(); } }); // Forward button ImageButton forward = new ImageButton(ctx.getContext()); RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2); forward.setLayoutParams(forwardLayoutParams); forward.setContentDescription("Forward Button"); forward.setId(3); try { forward.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_right.png")); } catch (IOException e) { Log.e(LOG_TAG, e.getMessage(), e); } forward.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { goForward(); } }); // Edit Text Box edittext = new EditText(ctx.getContext()); RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1); textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5); edittext.setLayoutParams(textLayoutParams); edittext.setId(4); edittext.setSingleLine(true); edittext.setText(url); edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI); edittext.setImeOptions(EditorInfo.IME_ACTION_GO); edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE edittext.setOnKeyListener(new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { // If the event is a key-down event on the "enter" button if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { navigate(edittext.getText().toString()); return true; } return false; } }); // Close button ImageButton close = new ImageButton(ctx.getContext()); RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); close.setLayoutParams(closeLayoutParams); forward.setContentDescription("Close Button"); close.setId(5); try { close.setImageBitmap(loadDrawable("www/childbrowser/icon_close.png")); } catch (IOException e) { Log.e(LOG_TAG, e.getMessage(), e); } close.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { closeDialog(); } }); // WebView webview = new WebView(ctx.getContext()); webview.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); webview.setWebChromeClient(new WebChromeClient()); WebViewClient client = new ChildBrowserClient(edittext); webview.setWebViewClient(client); WebSettings settings = webview.getSettings(); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setBuiltInZoomControls(true); settings.setPluginsEnabled(true); settings.setDomStorageEnabled(true); webview.loadUrl(url); webview.setId(6); webview.getSettings().setLoadWithOverviewMode(true); webview.getSettings().setUseWideViewPort(true); webview.requestFocus(); webview.requestFocusFromTouch(); // Add the back and forward buttons to our action button container layout actionButtonContainer.addView(back); actionButtonContainer.addView(forward); // Add the views to our toolbar toolbar.addView(actionButtonContainer); toolbar.addView(edittext); toolbar.addView(close); // Don't add the toolbar if its been disabled if (getShowLocationBar()) { // Add our toolbar to our main view/layout main.addView(toolbar); } // Add our webview to our main view/layout main.addView(webview); WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.copyFrom(dialog.getWindow().getAttributes()); lp.width = WindowManager.LayoutParams.FILL_PARENT; lp.height = WindowManager.LayoutParams.FILL_PARENT; dialog.setContentView(main); dialog.show(); dialog.getWindow().setAttributes(lp); } private Bitmap loadDrawable(String filename) throws java.io.IOException { InputStream input = ctx.getAssets().open(filename); return BitmapFactory.decodeStream(input); } }; this.ctx.runOnUiThread(runnable); return ""; } /** * Create a new plugin result and send it back to JavaScript * * @param obj a JSONObject contain event payload information */ private void sendUpdate(JSONObject obj, boolean keepCallback) { if (this.browserCallbackId != null) { PluginResult result = new PluginResult(PluginResult.Status.OK, obj); result.setKeepCallback(keepCallback); this.success(result, this.browserCallbackId); } } /** * The webview client receives notifications about appView */ public class ChildBrowserClient extends WebViewClient { EditText edittext; /** * Constructor. * * @param mContext * @param edittext */ public ChildBrowserClient(EditText mEditText) { this.edittext = mEditText; } /** * Notify the host application that a page has started loading. * * @param view The webview initiating the callback. * @param url The url of the page. */ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); String newloc; if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) { newloc = url; } else { newloc = "http://" + url; } if (!newloc.equals(edittext.getText().toString())) { edittext.setText(newloc); } try { JSONObject obj = new JSONObject(); obj.put("type", LOCATION_CHANGED_EVENT); obj.put("location", url); sendUpdate(obj, true); } catch (JSONException e) { Log.d("ChildBrowser", "This should never happen"); } } } }     2.引入.js文件   /* * cordova is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2011, IBM Corporation */ /** * Constructor */ function ChildBrowser() { }; ChildBrowser.CLOSE_EVENT = 0; ChildBrowser.LOCATION_CHANGED_EVENT = 1; /** * Display a new browser with the specified URL. * This method loads up a new web view in a dialog. * * @param url The url to load * @param options An object that specifies additional options */ ChildBrowser.prototype.showWebPage = function(url, options) { alert("enter"); if (options === null || options === "undefined") { var options = new Object(); options.showLocationBar = true; } cordova.exec(this._onEvent, this._onError, "ChildBrowser", "showWebPage", [url, options]); }; /** * Close the browser opened by showWebPage. */ ChildBrowser.prototype.close = function() { cordova.exec(null, null, "ChildBrowser", "close", []); }; /** * Display a new browser with the specified URL. * This method starts a new web browser activity. * * @param url The url to load * @param usecordova Load url in cordova webview [optional] */ ChildBrowser.prototype.openExternal = function(url, usecordova) { if (usecordova === true) { navigator.app.loadUrl(url); } else { cordova.exec(null, null, "ChildBrowser", "openExternal", [url, usecordova]); } }; /** * Method called when the child browser has an event. */ ChildBrowser.prototype._onEvent = function(data) { if (data.type == ChildBrowser.CLOSE_EVENT && typeof window.plugins.childBrowser.onClose === "function") { window.plugins.childBrowser.onClose(); } if (data.type == ChildBrowser.LOCATION_CHANGED_EVENT && typeof window.plugins.childBrowser.onLocationChange === "function") { window.plugins.childBrowser.onLocationChange(data.location); } }; /** * Method called when the child browser has an error. */ ChildBrowser.prototype._onError = function(data) { if (typeof window.plugins.childBrowser.onError === "function") { window.plugins.childBrowser.onError(data); } }; /** * Maintain API consistency with iOS */ ChildBrowser.install = function(){ return window.plugins.childBrowser; }; /** * Load ChildBrowser */ cordova.addConstructor(function() { cordova.addPlugin("childBrowser", new ChildBrowser()); });   3.在plugin.xml中定义tag   <plugin name="ChildBrowser" value="com.tricedesigns.ChildBrowser"/>   4.定义调用的js方法   function bbb(){ window.plugins.childBrowser.showWebPage("https://api.weibo.com/2/oauth2/authorize?client_id=1996979548&response_type=token&display=js&redirect_uri=https://api.weibo.com/oauth2/xd.html", { showLocationBar: true }); }   5.ok,run 源代码可去我的qq群中下载:250395324

andriod, phonegap

Andriod phonegap(Cordova)获得通话记录中的信息

17. 七月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 今天上海天气不错哈,18楼能望到东方明珠但看不到虹口足球场,着实有点遗憾~~不过今天phonegap有了大突破,又收获了一个获得通话log的插件。 1.引入.java和.js文件   /** * Example of Android PhoneGap Plugin */ package com.tricedesigns; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.EmptyStackException; import org.apache.cordova.api.PluginResult.Status; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.CallLog; import android.provider.Contacts; import android.provider.ContactsContract; import android.text.format.DateFormat; import android.util.Log; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; /** * Grab call log data * * @author James Hornitzky */ public class CallListPlugin extends Plugin { /** List Action */ private static final String ACTION = "list"; private static final String CONTACT_ACTION = "contact"; private static final String SHOW_ACTION = "show"; private static final String TAG = "CallListPlugin"; /* * (non-Javadoc) * * @see com.phonegap.api.Plugin#execute(java.lang.String, * org.json.JSONArray, java.lang.String) */ @Override public PluginResult execute(String action, JSONArray data, String callbackId) { Log.d(TAG, "Plugin Called"); PluginResult result = null; if (ACTION.equals(action)) { try { int limit = -1; //obtain date to limit by if (!data.isNull(0)) { String d = data.getString(0); Log.d(TAG, "Time period is: " + d); if (d.equals("week")) limit = -7; else if (d.equals("month")) limit = -30; else if (d.equals("all")) limit = -1000000; // LOL } //turn this into a date Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.DAY_OF_YEAR, limit); Date limitDate = calendar.getTime(); String limiter = String.valueOf(limitDate.getTime()); //now do required search JSONObject callInfo = getCallListing(limiter); Log.d(TAG, "Returning " + callInfo.toString()); //Log.d(TAG,callInfo); result = new PluginResult(Status.OK, callInfo); } catch (JSONException jsonEx) { Log.d(TAG, "Got JSON Exception " + jsonEx.getMessage()); result = new PluginResult(Status.JSON_EXCEPTION); } } else if (SHOW_ACTION.equals(action)) { try { if (!data.isNull(0)) { viewContact(data.getString(0)); } } catch (JSONException jsonEx) { Log.d(TAG, "Got JSON Exception " + jsonEx.getMessage()); result = new PluginResult(Status.JSON_EXCEPTION); } catch (Exception e) {} } else if (CONTACT_ACTION.equals(action)) { try { String contactInfo = getContactNameFromNumber(data.getString(0)); Log.d(TAG, "Returning " + contactInfo.toString()); result = new PluginResult(Status.OK, contactInfo); } catch (JSONException jsonEx) { Log.d(TAG, "Got JSON Exception " + jsonEx.getMessage()); result = new PluginResult(Status.JSON_EXCEPTION); } } else { result = new PluginResult(Status.INVALID_ACTION); Log.d(TAG, "Invalid action : " + action + " passed"); } return result; } /** * Gets the Directory listing for file, in JSON format * * @param file * The file for which we want to do directory listing * @return JSONObject representation of directory list. e.g * {"filename":"/sdcard" * ,"isdir":true,"children":[{"filename":"a.txt" * ,"isdir":false},{...}]} * @throws JSONException */ private JSONObject getCallListing(String period) throws JSONException { JSONObject callLog = new JSONObject(); String[] strFields = { android.provider.CallLog.Calls.DATE, android.provider.CallLog.Calls.NUMBER, android.provider.CallLog.Calls.TYPE, android.provider.CallLog.Calls.DURATION, android.provider.CallLog.Calls.NEW, android.provider.CallLog.Calls.CACHED_NAME, android.provider.CallLog.Calls.CACHED_NUMBER_TYPE, android.provider.CallLog.Calls.CACHED_NUMBER_LABEL }; try { Cursor callLogCursor = ctx.getContentResolver().query( android.provider.CallLog.Calls.CONTENT_URI, strFields, CallLog.Calls.DATE + ">?", new String[] {period}, android.provider.CallLog.Calls.DEFAULT_SORT_ORDER); int callCount = callLogCursor.getCount(); if (callCount > 0) { JSONObject callLogItem = new JSONObject(); JSONArray callLogItems = new JSONArray(); callLogCursor.moveToFirst(); do { callLogItem.put("date", callLogCursor.getLong(0)); callLogItem.put("number", callLogCursor.getString(1)); callLogItem.put("type", callLogCursor.getInt(2)); callLogItem.put("duration", callLogCursor.getLong(3)); callLogItem.put("new", callLogCursor.getInt(4)); callLogItem.put("cachedName", callLogCursor.getString(5)); callLogItem.put("cachedNumberType", callLogCursor.getInt(6)); //callLogItem.put("name", getContactNameFromNumber(callLogCursor.getString(1))); //grab name too callLogItems.put(callLogItem); callLogItem = new JSONObject(); } while (callLogCursor.moveToNext()); callLog.put("rows", callLogItems); } callLogCursor.close(); } catch (Exception e) { Log.d("CallLog_Plugin", " ERROR : SQL to get cursor: ERROR " + e.getMessage()); } return callLog; } /** * Show contact data based on id * @param number */ private void viewContact(String number) { Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, Uri.parse(String.format("tel: %s", number))); this.ctx.startActivity(i); } /** * Util method to grab name based on number * */ private String getContactNameFromNumber(String number) { // define the columns I want the query to return String[] projection = new String[] { Contacts.Phones.DISPLAY_NAME, Contacts.Phones.NUMBER }; // encode the phone number and build the filter URI Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, Uri.encode(number)); // query time Cursor c = ctx.getContentResolver().query(contactUri, projection, null, null, null); // if the query returns 1 or more results // return the first result if (c.moveToFirst()) { String name = c.getString(c.getColumnIndex(Contacts.Phones.DISPLAY_NAME)); c.deactivate(); return name; } // return the original number if no match was found return number; } }     calllog.js文件放在assert-》www文件中   var CallLog ={ list:function(params, successCallback, failureCallback) { return cordova.exec(successCallback, failureCallback, 'CallListPlugin', 'list', [ params ]); }, contact:function(params, successCallback, failureCallback) { return cordova.exec(successCallback, failureCallback, 'CallListPlugin', 'contact', [ params ]); }, show:function(params, successCallback, failureCallback) { return cordova.exec(successCallback, failureCallback, 'CallListPlugin', 'show', [ params ]); } };       然后开始例行公事: 1.在plugin.xml中添加语句(记得修改packageName) <plugin name="CallListPlugin" value="com.tricedesigns.CallListPlugin"/>   2.定义调用的js function aaa(){ CallLog.list('all', function(data){ //console.log(data); alert(data.rows.length); alert(data.rows[0].cachedName) //for(var i=0;i<=data.rows.length) //alert(data.rows.cachedName); }, function(){}); } 效果如下:项目下载可进我的qq群共享(224711028 )  

phonegap

phonegap(Cordova)弹出软件盘功能

16. 七月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 貌似没什么用的软件盘弹出的功能,利用pg怎么去实现 1.首先是phonegap必备的两个文件,分别是本地.java代码SoftKeyBoard.java   package com.tricedesigns; import org.json.JSONArray; import android.content.Context; import android.view.inputmethod.InputMethodManager; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; public class SoftKeyBoard extends Plugin { public SoftKeyBoard() { } public void showKeyBoard() { InputMethodManager mgr = (InputMethodManager) ((Context) this.ctx).getSystemService(Context.INPUT_METHOD_SERVICE); mgr.showSoftInput(webView, InputMethodManager.SHOW_IMPLICIT); ((InputMethodManager) ((Context) this.ctx).getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(webView, 0); } public void hideKeyBoard() { InputMethodManager mgr = (InputMethodManager) ((Context) this.ctx).getSystemService(Context.INPUT_METHOD_SERVICE); mgr.hideSoftInputFromWindow(webView.getWindowToken(), 0); } public boolean isKeyBoardShowing() { int heightDiff = webView.getRootView().getHeight() - webView.getHeight(); return (100 < heightDiff); // if more than 100 pixels, its probably a keyboard... } public PluginResult execute(String action, JSONArray args, String callbackId) { if (action.equals("show")) { this.showKeyBoard(); return new PluginResult(PluginResult.Status.OK, "done"); } else if (action.equals("hide")) { this.hideKeyBoard(); return new PluginResult(PluginResult.Status.OK); } else if (action.equals("isShowing")) { return new PluginResult(PluginResult.Status.OK, this.isKeyBoardShowing()); } else { return new PluginResult(PluginResult.Status.INVALID_ACTION); } } }     2.(.js文件share.js) var SoftKeyBoard={ skbShow:function(win, fail){ return cordova.exec( function (args) { if(win !== undefined) { win(args); } }, function (args) { if(fail !== undefined) { fail(args); } }, "SoftKeyBoard", "show", []); } } /*function SoftKeyBoard() {} SoftKeyBoard.prototype.show = function(win, fail) { return PhoneGap.exec( function (args) { if(win !== undefined) { win(args); } }, function (args) { if(fail !== undefined) { fail(args); } }, "SoftKeyBoard", "show", []); }; SoftKeyBoard.prototype.hide = function(win, fail) { return PhoneGap.exec( function (args) { if(win !== undefined) { win(args); } }, function (args) { if(fail !== undefined) { fail(args); } }, "SoftKeyBoard", "hide", []); }; SoftKeyBoard.prototype.isShowing = function(win, fail) { return PhoneGap.exec( function (args) { if(win !== undefined) { win(args); } }, function (args) { if(fail !== undefined) { fail(args); } }, "SoftKeyBoard", "isShowing", []); }; PhoneGap.addConstructor(function() { PhoneGap.addPlugin('SoftKeyBoard', new SoftKeyBoard()); PluginManager.addService("SoftKeyBoard","com.zenexity.SoftKeyBoardPlugin.SoftKeyBoard"); }); */     3.然后我们在phonegap项目中添加上述两个文件   4.在plugin.xml中添加语句(记得修改packageName) <plugin name="SoftKeyBoard" value="com.tricedesigns.SoftKeyBoard"/>   5.定义调用的js function keyBoardClick(){ SoftKeyBoard.skbShow(function () { // success },function () { // fail }); }   效果如下:项目下载可进我的qq群共享(224711028 )  

phonegap

Phonegap(Cordova自定义插件)实现分享功能

16. 七月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 利用phonegap(Cordova)的拓展我们可以利用本地的安卓或者ios代码拓展web App的功能,下面就来介绍一个分享插件,利用这一功能我们可以自定义好短信的内容然后发送给你的好友。 1.首先介绍phonegap必备的两个文件,分别是本地.java代码Share.java   /** * * Phonegap share plugin for Android * Kevin Schaul 2011 * */ package com.tricedesigns; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Intent; import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; public class Share extends Plugin { @Override public PluginResult execute(String action, JSONArray args, String callbackId) { try { JSONObject jo = args.getJSONObject(0); doSendIntent(jo.getString("subject"), jo.getString("text")); return new PluginResult(PluginResult.Status.OK); } catch (JSONException e) { return new PluginResult(PluginResult.Status.JSON_EXCEPTION); } } private void doSendIntent(String subject, String text) { Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND); sendIntent.setType("text/plain"); sendIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, text); this.cordova.startActivityForResult(this, sendIntent, 0); } }   2.(.js文件share.js)   /** * * Phonegap share plugin for Android * Kevin Schaul 2011 * */ var Share = { show:function(content, success, fail) { return cordova.exec( function(args) { success(args); }, function(args) { fail(args); }, 'Share', '', [content]); } };     3.然后我们在phonegap项目中添加上述两个文件   4.在plugin.xml中添加语句(记得修改packageName) <plugin name="Share" value="com.schaul.plugins.share.Share"/>   5.定义调用的js   function shareClick(){ Share.show({ subject: 'I like turtles', text: 'http://www.mndaily.com'}, function() {}, // Success function function() {alert('Share failed')} // Failure function ); } 效果如下:项目下载可进我的qq群共享(224711028 )  

phonegap

推荐一个nb老外的博客文章(phonegap专家)

14. 七月 2012
  Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入  发现一个很nb的老外博客http://www.adobe.com/devnet/author_bios/andrew_trice.html专门研究phonegap的  

phonegap

phonegap插件下载

14. 七月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 https://github.com/phonegap/phonegap-plugins

phonegap

开发第一个phonegap插件

13. 七月 2012
今天上海雷阵雨,在18楼看着窗外真是蛮壮观的,哈哈~~发现了一篇老外关于拓展phonegap的文章: http://www.adobe.com/devnet/html5/articles/extending-phonegap-with-native-plugins-for-android.html 照着跑了一下,发现真是蛮神奇的,我们所要做的其实就几步(在配置好了phonegap开发环境的情况下) 1.首先在asset-》www文件下新建一个.js文件(叫HelloPlugin.js)   var HelloPlugin = { callNativeFunction: function (success, fail, resultType) { return cordova.exec( success, fail, "com.tricedesigns.HelloPlugin", "nativeAction", [resultType]); } };   2.新建一个本地的java class,类名为HelloPlugin,继承org.apache.cordova.api.Plugin,package名字为com.tricedesigns 3.copy下列代码进入到这个class public class HelloPlugin extends Plugin { public static final String NATIVE_ACTION_STRING="nativeAction"; public static final String SUCCESS_PARAMETER="success"; @Override public PluginResult execute(String action, JSONArray data, String callbackId) { Log.d("HelloPlugin", "Hello, this is a native function called from PhoneGap/Cordova!"); //only perform the action if it is the one that should be invoked if (NATIVE_ACTION_STRING.equals(action)) { String resultType = null; try { resultType = data.getString(0); } catch (Exception ex) { Log.d("HelloPlugin", ex.toString()); } if (resultType.equals(SUCCESS_PARAMETER)) { return new PluginResult(PluginResult.Status.OK, "Yay, Success!!!"); } else { return new PluginResult(PluginResult.Status.ERROR, "Oops, Error :("); } } return null; } }     4.在index.html中引入js标签 <script type="text/javascript" charset="utf-8" src="HelloPlugin.js"></script>   5.helloPlugin.js中添加回调的函数 function callNativePlugin( returnSuccess ) { HelloPlugin.callNativeFunction( nativePluginResultHandler, nativePluginErrorHandler, returnSuccess ); } function nativePluginResultHandler (result) { alert("SUCCESS: \r\n"+result ); } function nativePluginErrorHandler (error) { alert("ERROR: \r\n"+error ); }     6.修改html上body的内容 <body onload="onBodyLoad()"> <h1>Hey, it's Cordova!</h1> <button onclick="callNativePlugin('success');">Click to invoke the Native Plugin with an SUCCESS!</button> <button onclick="callNativePlugin('error');">Click to invoke the Native Plugin with an ERROR!</button> </body>     7.运行即可,如果在logcat中发现以下文字hello,this is a native function.......表示拓展已经成功了。 项目代码可进我的qq群共享中拿 Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入  

phonegap

基于phonegap框架在ios和andriod上实现推送

12. 七月 2012
Phonegap Andriod 交流 QQ 群 250395324欢迎您的加入 查看一天的资料,老外的见解无非是两种,一种是使用pushWoosh这种免费的第三方框架,但是其利用google云的服务,不管怎么配置appId和aplicationId。。。。。。。。都没有办法正确的叫到推送。。。。绝望了。。。。 参考www.pushwoosh.com   第二种使用ua框架实现 参考文章https://github.com/pmarti/phonegap-ua-android-sample(andriod) ios(https://github.com/phonegap/phonegap-plugins/tree/master/iOS/PushNotification) 但是遇到白屏的一些情况。。。。。。无法调通,更可恶的是这个推送服务要收费的,有更好的框架大家可以留言记得提醒lz了。 最后实在不行了。。。。。   第三种方法:js轮询 setInterval(function(){ alert("sdfsddas"); }, 10000);   第四种可能想到最好的办法 用安卓的service轮询请求到消息后用系统通知控件显示出来.  

phonegap

在andriod以及ios中拓展phonegap(无敌了)

10. 七月 2012
原文1:http://www.adobe.com/devnet/html5/articles/extending-phonegap-with-native-plugins-for-android.html 原文2:http://www.adobe.com/devnet/html5/articles/extending-phonegap-with-native-plugins-for-ios.html  

phonegap

phonegap地理位置不准原因

5. 七月 2012
Geolocation提供设备的位置信息,例如经度和纬度。位置信息的常见来源包括全球定位系统(GPS),以及通过诸如IP地址、RFID、WiFi和蓝牙的MAC地址、和GSM/CDMA手机ID的网络信号所做的推断。不能保证该API返回的是设备的真实位置信息。   这个API是基于W3C Geo location API Specification实现的。有些设备已经提供了对该规范的实现,对于这些设备采用内置实现而非使用PhoneGap的实现。对于没有地理位置支持的设备,PhoneGap的实现应该是完全兼容W3C规范。

phonegap