Posts Tagged ‘Flex 4’

AIR/Flex RegEx Testing Tool

Tuesday, July 13th, 2010

在Flex/AS 3里面使用正则做验证有时候还是比较方便的,要是验证正则表达式是否正确,推荐使用RegEx Testing Tool这个小工具,不仅有验证,还有一些常用的正则表达式写法。
在线的Flex版本地址:http://gskinner.com/RegExr/
AIR的安装地址:http://gskinner.com/RegExr/desktop
AIR文件的下载地址:http://gskinner.com/RegExr/desktop/RegExr.air

Flex 4 实现 Vimeo.com UI

Friday, January 22nd, 2010

vimeo.com虽然在国内无法访问了,但是它的UI依旧让人念念不忘。最近发现了一个用Flex 4实现的Vimeo样式的菜单和搜索框,模仿的相当细致。并且内置了很多调色板,可以根据自己的需要进行修改。


Demo的访问地址:http://anirudhsasikumar.net/examples/scopedsearch/
原作者的Blog地址:http://anirudhs.chaosnet.org/blog/2009.12.09.html
下载地址:http://www.douhua.im/downloads/ScopedNavigation.zip

ActionScript/Flex 使用 as3flickrlib 访问 Flickr.com 的资源

Friday, January 22nd, 2010

Flickr.com是业内最好、最有名的图片网站,它提供了丰富的API让开发者创建各种应用。Adobe也很早就发布了ActionScript的Library(as3flickrlib)帮助Flex/Flash开发者开发基于Flash的应用。2007年as3flickrlib的托管从Adobe迁移到Google Code中,到本文撰写时候的最后更新是在2008年8月28日,本文的所有示例代码都是基于Subversion中的最后代码(版本r37)。as3flickrlib的代码需要as3corelib的支持,同时也是学习使用as3corelib编写WebService API的好教材。

使用Flickr的API还是先解决认证的问题,Flickr的认证没有采用OAuth但是方式相似,认证过程相对简单一些。这里采用的是Desktop Application的认证流程:

  • 1. 设定认证的权限,在Flickr.com取得一个frob的参数。
  • 2. 根据frob进行签名获取认证的URL,在浏览器中打开由用户进行授权。
  • 3. 完成授权后,使用frob换取认证的token。
  • 声明一个FlickrService的对象

    f = new FlickrService('xxxxxxxxxxxxxxxxx'); #API Key
    f.secret = 'xxxxxxxxxxxxxxxxx'; #API Secret Key

    获取frob,成功后打开授权页面:

    f.addEventListener(FlickrResultEvent.AUTH_GET_FROB, function(e : FlickrResultEvent) : void{
        if(e.success){
    	frob = e.data.frob as String;
    	var auth_url : String = f.getLoginURL(frob, AuthPerm.READ); #申请只读权限
    	avigateToURL(new URLRequest(auth_url), "_blank" ); #打开授权的URL
        }
    });
    f.auth.getFrob();

    授权完成后,frob交换token:

    f.addEventListener(FlickrResultEvent.AUTH_GET_TOKEN, function(e : FlickrResultEvent) : void{
    	if(e.success){
    		var authResult : AuthResult = AuthResult(e.data.auth);
    		f.token = authResult.token;
    		username = authResult.user.username;
    		nsid = authResult.user.nsid;
    	}else{
    		Alert.show('Exchange Token Error');
    	}
    });
    f.auth.getToken(frob);

    认证授权后,就可以使用FlickrService访问用户的资源,包括那些private的数据。下面是使用一些功能的示例:

    //根据email地址搜索用户
    f.addEventListener(FlickrResultEvent.PEOPLE_FIND_BY_EMAIL, function(e : FlickrResultEvent) : void{
      if(e.success == true){
        var user : User = e.data.user;
        Alert.show(user.username, user.nsid);
    }
    f.people.findByEmail('xxxxxxx@gmail.com');
     
    //根据用户名搜索用户
    f.addEventListener(FlickrResultEvent.PEOPLE_FIND_BY_USERNAME, function(e : FlickrResultEvent) : void{
      if(e.success == true){
        var user : User = e.data.user;
        Alert.show(user.username, user.nsid);
      }
    });
    f.people.findByUsername('xxxxxxxxxxx');
     
    //获取一张图片的信息
    f.addEventListener(FlickrResultEvent.PHOTOS_GET_INFO, function(e : FlickrResultEvent) : void{
      if(e.success){
        var photo : Photo = e.data.photo;
        Alert.show(photo.title, photo.dateAdded.toString());
      }
    });
    f.photos.getInfo('xxxxxxx'); //图片ID
     
    //获取用户所有公开的图片
    f.addEventListener(FlickrResultEvent.PEOPLE_GET_PUBLIC_PHOTOS, function(e : FlickrResultEvent) : void{
      if(e.success){
        var s : PagedPhotoList = e.data.photos;
        for each(var p : Photo in s.photos){
          var url : String = 'http://farm' + p.farmId.toString() + '.static.flickr.com/' 
                                   + p.server.toString() + '/' + p.id + '_' + p.secret + '.jpg';
          var thumbnail : String = 'http://farm' + p.farmId.toString() + '.static.flickr.com/' 
                                   + p.server.toString() + '/' + p.id + '_' + p.secret + '_t.jpg';
     
          Alert.show(url, thumbnail);
        }
      }
    });
    f.people.getPublicPhotos('xxxxxx'); //用户ID,既NSID

    要解释的是如何根据数据拼接图片的访问地址,Flickr的官方文档在http://www.flickr.com/services/api/misc.urls.html

    http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg
    http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg
    http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)

    farm-id、server-id、id、o-secret和sercret在Photo的对象中都有封装,将值带入上面的格式中就可以得到需要的URL。mstb这个值没有封装,第3种格式种的图片类型由Photo的originalFormat属性可以得到。如果需要得到不同大小的图片,可以修改第3种格式中的o参数

    s	75x75 像素
    t	thumbnail,最长的边为100像素
    m	small, 最长的边为200像素
    -	medium, 最长的边为500像素
    b	large, 最长的边为1024像素 (只有原始图片非常大的时候才有这种格式)
    o	图片原始大小

    本文涉及的是只读flickr数据,以后会发一个使用as3flickrlib上传图片的文章。

    编译Flex应用中加入编译条件

    Tuesday, January 12th, 2010

    开发Flex应用的时候,很需要像开发C/C++程序那样设置Debug的编译。一直都没有找到很好的办法,今天终于在Cookbook上看到了一个解决方案。现整理一下中文,供大家研究。

    在命令行编译的时候加入指定的参数:

    -define=CONFIG::DEBUG,true

    需要在应用的flex-config.xml文件中预定义CONFIG::DEBUG这个变量:

    <compiler>
        <define>
            <name>CONFIG::DEBUG</name>
            <value>true</value>
        </define>
    </compiler>

    如果是用Ant编译的话,需要在mxmlc这个task里面定义CONFIG::DEBUG变量:

        <mxmlc>
            <define name="CONFIG::DEBUG" value="true"/>
        </mxmlc>

    在AS代码中这样使用CONFIG::DEBUG:

    package  {
     
        public class Logger extends Sprite 
        {    
            public function Logger() {
                super();
            }
            public static function log(...message):void {
                CONFIG::DEBUG{  
                  trace.apply(null,message);
     
                }
            }
        }
    }

    使用Google Analytics追踪和分析网站Flash的点击

    Tuesday, January 12th, 2010

    Google Analytics是当前最流行的网站流量统计和分析的工具,加上与Google Adwords和Google Andsense的集成,使它成为企业级的网站分析工具。在前面的《Flex/ActionScript访问Google Analytics数据》的文章中,介绍了如何将Google Analytics的统计数据导出,在Flex/Flash的应用里面展示。现在来讨论如何统计记录网站上Flash的点击,这对于使用Flex/Flash开发网站应用是十分重要的。

    在Google Analytics刚发布的时候,想统计站点内Flash的点击是做不到的。后有开发者通过分析Google的Track脚本,在Flash内部使用ExternalInterface来调用Google Analytics的Track代码实现统计。Google Analytics发布了API后,包括了一个用于统计Flash访问的项目gaforflash,其托管在Google Code上并且是Open Source的。其实现的Track功能和ga.js是完全一样的,可以在Flash和Flex的开发环境中使用,但是这个项目是基于ActionScript 3的版本。

    gaforflash的使用方式有两种:Bridge Model和AS 3 Model。Bridge Model是指站点的页面已经实现了Google Analytics的ga.js的统计,想要统计页面里embed的Flash的点击,使用gaforflash项目调用外部的ga.js里面的函数来实现统计,所以称为“桥”模式。使用Bridge Model要注意的是embed Flash的标签里面allowScriptAccess的属性设为True,否则无法调用外部的js脚本;AS 3 Model是纯ActionScript的模式,如果你的Flash会Embed到不同站点去,也就是有cross domain的情况产生,使用AS 3 Model就会没有问题,AS 3 Model使用shareobject的来存储访问者session等信息,如果Track的Flash也使用了shareobject,请注意命名。

    gaforflash统计的信息目前有两种:Page Views和Event。如果是全站的Flash适用于Page View模式;如果是一个简单的播放器嵌在HTML页面,适用于Event模式。

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    xmlns:ga4flash="com.google.analytics.components.*" 
    addedToStage="trackInitialView()">
     
    <ga4flash:FlexTracker id="tracker" account="UA-111-222" 
    visualDebug="true" mode="AS3"/>
     
    <mx:Script>
        <![CDATA[
            public function trackInitialView():void
            {
                tracker.debug.minimizedOnStart = true;
                tracker.trackPageview("/pane1");
            }
        ]]>
    </mx:Script>
     
    <mx:Accordion x="31" y="43" width="380" height="423" 
    change="tracker.trackPageview('/pane' + String(event.newIndex+1));">
        <mx:Canvas label="Pane 1" width="100%" height="100%">
            <mx:Text text="Welcome to Pane 1."/>
        </mx:Canvas>
        <mx:Canvas label="Pane 2" width="100%" height="100%">
            <mx:Text text="Benvenuti a Pane 2."/>
        </mx:Canvas>
        <mx:Canvas label="Pane 3" width="100%" height="100%">
            <mx:Text text="Bienvenue à Pane 3!"/>
        </mx:Canvas>
    </mx:Accordion>
     
    </mx:Application>

    上面例子简单的实现了Page View的方式,当点击Accordion的不同标签时会按照每个标签一次Page View的方式记录到Google Analytics中。

    Event的代码也很简单,就是在按钮或者空间的点击事件中加入:

        tracker.trackEvent("Something", "Some Status", "Some Component");

    gafoflash还有很多其它的应用方式,譬如前面文章曾经介绍过《Adobe AIR Badge详解》,当你自定义一个Badge的时候有没有想过通过gaforflash的Event模式来确定你的AIR应用的安装情况呢?或者在一个Media Player中可以根据Event的统计来确定视频最佳的缓冲大小?

    参看文章:
    Google Analytics within Flex/Flash Applications
    Google Analytics Tracking for Adobe Flash

    Adobe AIR Badge详解

    Tuesday, January 12th, 2010

    Adobe AIR SDK的Sample目录里面有一个Badge的应用,这个应用提供两个功能:

  • 检验应用需要安装的AIR版本,如果当前系统版本不同,安装需要的版本。
  • 在线安装应用。
  • 可以用里面的程序来直接部署AIR应用,但是默认的Badge大小是217X180像素高。这个默认的UI不能满足更多定制的需求,需要对这个Flash进行改造,或者是通过定制标签来进行改变。
    Badge的参数很多,设置起来比较麻烦。可以用Grant Skinner的Badger来代替烦琐的编码工作。这个Badger是一个AIR的应用,里面详细的列出了Badge的参数,你可以选择需要绑定的AIR文件,然后export你需要的Badge。

    如果是还不能满足定制的需要,就需要重新编写Badge的代码了。如果使用Flex开发UI,就需要独立编写Badge的功能了。
    整个badge的关键是把adobe的air.swf下载到应用中,并作为一个Object帮助我们安装AIR环境和AIR应用。

        //air.swf地址
        private static const AIR_SWF_URL : String = 
            "http://airdownload.adobe.com/air/browserapi/air.swf";
     
        _airSWFLoader = new Loader();
        _airSWFLoader.contentLoaderInfo.addEventListener(
                              IOErrorEvent.IO_ERROR,handleAIRSWFError);
        _airSWFLoader.contentLoaderInfo.addEventListener(
                              Event.COMPLETE, handleAIRSWFInit);
        try
        {
            _airSWFLoader.load(new URLRequest(AIR_SWF_URL));
        }
        catch(e : *)
        {
            //自定义错误处理
        }

    handleAIRSWFInit函数里面处理下载AIR环境的检测。

            private function handleAIRSWFInit(event : Event) : void
            {
                _airSWF = event.target.content;
                try
                {
                    if(_airSWF.getStatus() == "installed")
                    {
                        //如果已经安装了应用,检测应用的版本。
                    }
                    else if(_airSWF.getStatus() == "unavailable")
                    {
                        //系统无法安装AIR环境。
                    }
                    else if(_airSWF.getStatus() == "available")
                    {
                        //系统可以安装AIR环境。
                    }
                }
                catch(e : *)
                {
                    //自定义错误信息
                }
            }

    检测应用是否安装的方法是通过air.swf的getApplicationVersion方法。

        //appID 是AIR应用的ID,在AIR的xml的配置文件有配置
        //appURL是AIR应用的路径。
        //appVersionCallback回调函数
        _airSWF.getApplicationVersion(appID, appURL, appVersionCallback);

    在回调函数中处理传进来的version参数,判断安装的情况。

            private function appVersionCallback(version : String) : void
            {
                if(version == null)
                {
                   //系统没有安装应用,进行安装操作。
                }
                else if(checkVersion(_appVersion, version) == 1)
                {
                    //系统安装了应用,但是版本比当前版本底,进行升级操作。
                }
                else
                {
                    //系统安装了应用,并且版本也与当前安装版本相同
                    //进行启动应用的操作。
                }
            }

    利用air.swf可以把安装和启动的功能集成到Flex的应用中,并且可以实现通过网站启动AIR应用。air.swf的功能还很多,后续的文章会进行详细的整理和说明,并且会Open Source一个ActionScript的封装类来继续简化使用,让开发者的精力集中到UI的设计中。

    Flex/ActionScript访问Google Analytics数据

    Tuesday, January 12th, 2010

    在Adobe AIR刚刚推出的时候,有一个Google Analytics AIR的应用非常吸引人。它读取Google Analytics的数据,运用各种Chart图表进行展示。后来这个项目从beta到正式版,名字也更换为Polaris,在Adobe的AIR Market上叫做Polaris For Google Anlytics。当它beta版本的时候,Google Analytics还没有发布相关的API,如何取得Google Analytics的数据着实让人迷惑。现在Google Analytics发布了Data Export API,利用这个API我们也可以读取Google Analytics的数据,做出自己的AIR应用。而且Google Analytics现在可以跟踪记录Flash/Flex应用里面的按钮或者链接事件,能够让我们更准确的记录和分析用户在网站上的行为。

    首先让我们了解一下Google Analytics Data Export API的一些读取限制政策,避免在开发的时候遇到访问数量的限制:

  • Google Analytics Data Export API的数量读取限制是针对一个站点的,即一个profile的。
  • 24小时内不能多于10,000个HTTP Request。
  • 10秒内不能多于100个HTTP Request。
  • 每次返回的XML数据中,不多于10,000个entry。
  • 开发一个AIR应用使用Google的API,首先要让用户通过Google账户的认证。Google API的认证有3种方式:

  • AuthSub Proxy Authentication
  • OAuth Authentication
  • ClientLogin Username/Password Authentication
  • 由于是AIR客户端,所以在这篇文章中我们选择ClientLogin Username/Password Authentication的认证方式,如果想开发基于OAuth的认证方式可以参考以前的文章《ActionScript For OAuth Step By Step》。要注意的是开发WEB Application的话,尽量选择OAuth的认证方式,因为Google安全策略的限制,ClientLogin的认证方式可能不能正常登陆。

    ClientLogin Username/Password Authentication是Google为客户端提供的认证方式,AIR的程序或者使用C#、C++开发的桌面程序都可以使用这种认证方式。这种认证方式很简单,就是向ClientLogin的认证地址POST一个表单,登录成功后从返回的数据中提取Auth这个变量。在以后的读取过程中在HTTP Header的Authorization中加入Auth这个变量就可以。

    ClientLogin的POST地址:
    https://www.google.com/accounts/ClientLogin

    表单有以下几项:

  • accountType 账户类型有GOOGLE、HOSTED和HOSTED_OR_GOOGLE。由于是Google Analytics和Google Account绑定在一起,所以这里是使用GOOGLE这个账户类型。
  • Email Google Analytics对应的Gmail账号。
  • Passwd Gmail密码
  • service 这里service是个固定值:analytics。如果想使用其它服务的API,这里应该是对应服务的名称。具体参考看Google Service Name List
  • source 可以是空的,也可以按照companyName-applicationName-versionID这个格式填写。
  • 现在我们写个函数登录和处理登录的返回值,取得想要的Auth变量。

    private function login(email : String, password : String) : void
    {
        var _loginRequest : URLRequest = new URLRequest();
        _loginRequest.url = 'https://www.google.com/accounts/ClientLogin';
        _loginRequest.method = URLRequestMethod.POST;
        _loginRequest.requestHeaders.push(
            new URLRequestHeader('Content-Type',
                'application/x-www-form-urlencoded'));
     
        var _data : URLVariables = new URLVariables();
        _data.accountType = 'GOOGLE';
        _data.Email = email;
        _data.Passwd = password;
        _data.service = 'analytics';
        _data.source = '';
     
        _loginRequest.data = _data;
     
        var _loader : URLLoader = new URLLoader();
        _loader.addEventListener(Event.COMPLETE, doLogin);
        _loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, doLogin);
        _loader.addEventListener(IOErrorEvent.IO_ERROR, doLogin);
        _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, doLogin);
        try
        {
            _loader.load(_loginRequest);
        }
        catch(error : Error)
        {
            Alert.show(error.message);
        }
    }
     
    private function doLogin(event : Event) : void
    {
        switch(event.type)
        {
            case Event.COMPLETE:
                authGoogle((event as Event).target.data);
            break;
            case HTTPStatusEvent.HTTP_STATUS:
                if((event as HTTPStatusEvent).status == 200)
                {
                    //do something
                }
            break;
            case IOErrorEvent.IO_ERROR:
                Alert.show((event as IOErrorEvent).text);
            break;
            case SecurityErrorEvent.SECURITY_ERROR:
                Alert.show((event as SecurityErrorEvent).text);
            break;
        }
    }
     
    private function authGoogle(data : Object) : void
    {
        var auth : String;
        var sid : String;
        var lsid : String;
     
        var _array : Array = (data as String).split('\n');
        for(var i : int = 0; i &lt; _array.length; i++)     {
            if((_array[i] as String).length &gt; 0)
            {
                var _param : URLVariables = new URLVariables()
                _param.decode(_array[i] as String);
                if(_param.hasOwnProperty('Auth'))
                    auth = _param['Auth'];
                if(_param.hasOwnProperty('SID'))
                    sid = _param['SID'];
                if(_param.hasOwnProperty('LSID'))
                    lsid = _param['LSID'];
            }
        }
        analyticsProfiles(auth);
    }

    一个Google Account可以添加多个Web Site的Profile,同时还可能会有别人share的Profile。所以在认证完成后,需要从Google Analytics取得Profile的列表。这个Profile列表是一个feed的xml文件,读取这个文件的时候要注意使用上一步的auth添加到HTTP的Header中。

    private function analyticsProfiles(auth : String) : void
    {
        var _profiles : URLRequest = new URLRequest();
        _profiles.url =
            'https://www.google.com/analytics/feeds/accounts/default';
        _profiles.method = URLRequestMethod.GET;
     
        var _auth : String = 'GoogleLogin auth=' + auth;
        _profiles.requestHeaders.push(
            new URLRequestHeader('Authorization', _auth));
     
        var _loader : URLLoader = new URLLoader();
        _loader.addEventListener(Event.COMPLETE, doProfile);
        try
        {
            _loader.load(_profiles);
        }
        catch(err : Error)
        {
            //Alert Error Message
        }
    }

    返回的xml数据:

    <?xml version='1.0' encoding='UTF-8'?>
    <feed xmlns='http://www.w3.org/2005/Atom' 
        xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' 
        xmlns:dxp='http://schemas.google.com/analytics/2009'>
        <id>
            http://www.google.com/analytics/feeds/accounts/genedna@gmail.com
        </id>
        <updated>2009-01-07T22:13:35.000-08:00</updated>
        <title type='text'>Profile list for genedna@gmail.com</title>
        <link rel='self' type='application/atom+xml' 
            href='http://www.google.com/analytics/feeds/accounts/default'/>
        <author>
            <name>Google Analytics</name>
        </author>
        <generator version='1.0'>Google Analytics</generator>
        <openSearch:totalResults>2</openSearch:totalResults>
        <openSearch:startIndex>1</openSearch:startIndex>
        <openSearch:itemsPerPage>2</openSearch:itemsPerPage>
        <entry>
            <id>
                http://www.google.com/analytics/feeds/accounts/ga:10200000
            </id>
            <updated>2008-08-11T19:21:12.000-07:00</updated>
            <title type='text'>www.douhua.im</title>
            <link rel='alternate' type='text/html' 
                href='http://www.google.com/analytics'/>
            <dxp:tableId>ga:10200000</dxp:tableId>
            <dxp:property name='ga:accountId' value='1400000'/>
            <dxp:property name='ga:accountName' value='Meaglith Ma'/>
            <dxp:property name='ga:profileId' value='10204831'/>
            <dxp:property name='ga:webPropertyId' value='UA-140000-15'/>
            <dxp:property name='ga:currency' value='USD'/>
            <dxp:property name='ga:timezone' value='Asia/Shanghai'/>
        </entry>
    </feed>

    返回的XML数据中tableId的值是一一对应Profile的,我们要访问douhua.im站点的访问数据情况,必须使用tableId这个值。读取Google Analytics Report的数据方式和读取Profile的方式一样,只是根据不同的参数读取不同的Report类型,目前可以访问的类型有Visitors、Traffic Sources 、Content、Goals和Ecommerce,访问的地址根据需要进行拼装,具体请参考Google Analytics Data Export API的Dimensions & Metrics Reference章节。示例:

    https://www.google.com/analytics/feeds/data?ids=ga:10204831&dimensions=ga:country,ga:city&metrics=ga:pageviews&start-date=2009-05-01&end-date=2009-05-31

    ActionScript For OAuth Step By Step

    Tuesday, January 12th, 2010

    OAuth协 议是现在众多网站提供API服务所选择的认证方式,是由Blaine Cook、Chris Messina、Larry Halff 及David Recordon共同发起,目的是为API服务提供一个安全、统一和开放的标准。众多知名的互联网公司选择了OAuth协议,如:Twitter、 Google和MySpace等;或者采用类似的认证方式,如flickr等。

    Twitter.com的API在初期使用了Basic Auth的认证方 式,第三方应用只要把用户名和密码进行BASE64加密,加入到HTTP的Header中就可以使用访问受限资源的API。随着用户和第三方应用越来越 多,把用户名和密码交给第三方应用的方式越来越不安全;如果修改了密码,要在众多的第三方程序间同步密码也变得非常麻烦。最终Twitter的开发团队终 于下决心采用OAuth协议对第三方应用进行授权,用户可以随时在设置里取消授权,就算修改了密码也不必为修改授权应用的密码操心。因为OAuth协议的 安全性和可靠性,在当今流行的网络服务提供商中绝大多数选择了它。

    Google也是OAuth的支持者,同时Google的DeWitt Clinton也为OAuth开发做出了巨大的贡献。我们用Google的OAuth Diagram来解释一下OAuth协议的流程。

    Web Application是指我们开发的第三方应用(在下面的讲解中我们假设开发了一个访问Google日历的Flash Application),Google Account Authentication指Google的认证服务(这里可以简单的理解为是OAuth认证服务器),Google Service Access指Google提供的API服务(下面的讲解中用Google Calendar Service来替代)。当用户使用我们假设的Flash应用访问Google日历时,需要通过OAuth协议来进行授权:

  • 首先Flash应用会访问Google OAuth认证服务,获取一个Request Token。
  • 从Google OAuth认证服务返回一个未授权的Request Token和对应的Request Token Secret。
  • Flash应用继续访问Google OAuth认证服务,要求对未授权的Request Token进行授权。
  • 如果用户已经登录了Google,会被重定向到一个请求授权页面。
  • 用户对应用进行授权。
  • 从Google OAuth认证服务返回已授权的Request Token和对应的Request Token Secret。
  • 用已授权的Request Token和对应的Request Token Secret在Google OAuth服务换取Access Token。
  • 从Google OAuth认证服务返回Access Token和对应的Access Token Secret。
  • 使用Access Token和对应的Access Token Secret访问Google Calendar服务。
  • 从Google Calendar返回用户的日历数据。
  • 在Twitter的众第三方多应用中,AIR类占了很大比例。其中最知名的就是Twhirl了,可惜的是它目前还不支持OAuth认证。要想为Twhirl加入OAuth其实很容易,看完下面的例子你也可以轻松做到。

    下面选择豆瓣的API为例,说明如何使用ActionScript 3开发基于OAuth认证的程序。示例程序可以在Google Code找到,在那里还有其它语言的OAuth例子。程序使用了一个OAuth的ActionScript 3类库,地址是 http://code.google.com/p/oauth-as3/,由于在OAuth的认证过程中需要HMAC-SHA1加密,所以程序还导入了一个加密功能的ActionScript 3类库,地址是 http://code.google.com/p/as3crypto/ 。

    首先要在豆瓣注册一个应用,会得到豆瓣分配的Key和Secret Key。代码里面先把Key、Secret Key和豆瓣的OAuth Service地址定义为常量。

    private static var DOUBAN_KEY : String = "xxxxxxxxxxxx";
    private static var DOUBAN_SECRET_KEY : String = "xxxxxxxxx";
     
    private static var DOUBAN_REQUEST : String =
            "http://www.douban.com/service/auth/request_token";
    private static var DOUBAN_AUTHORIZE : String =
            "http://www.douban.com/service/auth/authorize";
    private static var DOUBAN_ACCESS_TOKEN : String =
            "http://www.douban.com/service/auth/access_token";

    然后参照上图 流程1 编写一个获取Request Token访问地址的函数。

    /**
    * 生成获取Request Token的URL
    */
    private function generalDoubanRequestTokenURL() : String
    {
        var oauth : OAuthRequest = new OAuthRequest("GET", DOUBAN_REQUEST);
        oauth.consumer = new OAuthConsumer(DOUBAN_KEY, DOUBAN_SECRET_KEY);
        return oauth.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(),
                            OAuthRequest.RESULT_TYPE_URL_STRING);
    }

    调用这个函数会生成一个URL地址,把这个地址复制到浏览器里进行访问,会在页面中看到如下的显示:

    oauth_token=abcde&oauth_token_secret=fghil

    这两个参数的值就是未授权的Request Token和Request Token Secret。根据 流程3 对Request Token的进行授权的访问地址是
    http://www.douban.com/service/auth/authorize?oauth_token=abcde
    在浏览器里面输入这个地址后,就会进入到豆瓣的授权页面,如果用户还没有登录会先重定向到登录页面。

    用户对应用进行授权后,需要根据 流程7 换取Access Token。
    下面编写了一个函数生成换取Access Token访问地址的函数。

    /**
    * 生成换取Access Token使用的URL。
    */
    private function generalDoubanAccessTokenURL() : String
    {
        var oauth : OAuthRequest = new OAuthRequest("GET", DOUBAN_ACCESS_TOKEN);
        oauth.consumer = new OAuthConsumer(DOUBAN_KEY, DOUBAN_SECRET_KEY);
        oauth.token = new OAuthToken('abcde', 'fghil');
        return oauth.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(),
                      OAuthRequest.RESULT_TYPE_URL_STRING);
    }

    将上面函数生成的地址复制到浏览器后访问,会在页面中看到如下的显示:

    oauth_token=abcdww&oauth_token_secret=daseccs

    这次得到的就是访问受限资源的Access Token和Access Token Secret了,使用这两个参数就可以生成访问豆瓣受限资源的URL了。

    /**
    * General Access douban.com's resource url
    */
    private function generalPostURL(res:String, params:Object = null):String
    {
        var oauth : OAuthRequest = new OAuthRequest('POST', res);
        oauth.consumer = new OAuthConsumer(DOUBAN_KEY, DOUBAN_SECRET_KEY);
        oauth.token = new OAuthToken(‘abcdww’, ‘daseccs’);
        return oauth.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(),
                          OAuthRequest.RESULT_TYPE_URL_STRING);
    }

    访问的受限资源范围是根据授权用户的权限所限定的,授权用户无权访问的数据,第三方应用也无法访问。

    上面的代码中,OAuth的ActionScript 3类库帮助我们封装了很多细节,很容易地实现了OAuth协议的授权。把其中豆瓣OAuth服务地址替换成Twitter OAuth服务地址,就可以生成访问Twitter的OAuth程序。这些代码很容易运用到Flex/AIR的程序里,开发类似twhirl这样的第三方应用也变得简单了。

    Google Map API For Flash的3D视图功能

    Tuesday, January 12th, 2010

    Google Map API For Flash 1.16这个版本在2D地图之上增加了3D 视图,可以从3维的角度去观察和使用Google地图,在控制的操作上同Google Earth相同,这不仅让人无限遐想是不是Google Earth将来也会出Flash的版本?3D视图的Flash API为基于Google Map的应用开发带来了新的视角,可以基于一个平面的地图构建一个立体的虚拟城市,或者在3D视图上开发出一个Flash赛车游戏….

    想要使用3D版本的地图API,必须使用Flash Player 9 或者10的版本才可可以。由于3D的视角有利于发挥Flash Player 10的高级几何特性,所以官方推荐开发者尽量使用Flash Player 10。如果在代码中没有使用Flash Player 10才有的类,那么编译的程序在Flash Player 9和10上浏览都正常;如果使用10才有的类库,用Flash Player 9去浏览就会抛出一个错误,在这种情况下建议使用为9和10维护不同的版本。需要将Flex SDK的版本更新到3.3以上,支持对Flash Player 10的编译。

    下面从最简单的一个例子开始,了解和使用Google Map API For Flash的3D视图功能。

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
        layout="absolute"> 
        <maps:Map3D xmlns:maps="com.google.maps.*" 
            mapevent_mappreinitialize="onMapPreinitialize(event)" 
            id="map" width="100%" height="100%" key="abc"/>
        <mx:Script>  
            <![CDATA[  
                import com.google.maps.LatLng;  
                import com.google.maps.Map3D;  
                import com.google.maps.MapEvent;  
                import com.google.maps.MapOptions;  
                import com.google.maps.MapType;  
                import com.google.maps.View;  
                import com.google.maps.geom.Attitude;  
     
                private function onMapPreinitialize(event:MapEvent):void 
                {    
                    var myMapOptions:MapOptions = new MapOptions();    
                    myMapOptions.zoom = 12;    
                    myMapOptions.center = 
                          new LatLng(40.756054, -73.986951);    
                    myMapOptions.mapType = MapType.NORMAL_MAP_TYPE;    
                    myMapOptions.viewMode = View.VIEWMODE_PERSPECTIVE;    
                    myMapOptions.attitude = new Attitude(20,30,0);    
                    map.setInitOptions(myMapOptions);  
                }  
            ]]>
        </mx:Script>
    </mx:Application>

    在代码里可以看到,要使用Map3D对象代替Map对象,然后在Map3D对象的MAP_PREINITIALIZE事件中初始化这个对象。除了设定viewMode的值为View.VIEWMODE_PERSPECTIVE,还要设置一个Map3D对象的属性Attitude。 Attitude是根据航空学中飞行器在飞行过程中视角来定义地图的视角,有roll、yaw和pitch三个值。从下面的张图片中很容易理解这三个角度和地图的关系。

    要增加地图的3D控制,就需要在Map3D的MAP_READY的事件中增加NavigationControl。

    map.addControl(new NavigationControl());

    在上面的代码中为Map3D的viewMode指定一个VIEWMODE_PERSPECTIVE,意思就是为地图建立一个Attitude定义的摄像机视角来看地图。viewMode如果设定为VIEWMODE_2D,就是建立一个2D视角的地图;如果设定为VIEWMODE_ORTHOGONAL,就是定义一个倾斜的固定视角。
    在Map3D提供一个flyTo()的函数,可以让用户从当前地点移动到另一个经纬度,实现和Google Earth一样的移动效果。

    map.flyTo(new LatLng(40.756054, -73.986951), 12, new Attitude(20,30,0), 3);

    更多的实例和说明请访问http://code.google.com/apis/maps/documentation/flash/3d-maps.html

    [Google AppEngine] 中使用Flex实现Google Account SignIn/SignOut

    Monday, January 4th, 2010

    在AppEngine中可以使用Google Account实现认证。如果使用Flex Application作为UI,使用按钮实现Sign In/Sign Out需要把URL通过HTML页面的FlashVars传递到Flex Application中。但是这个URL的值是几个HTTP地址经过&符号拼接而成,在Flex里通过FlexGlobals.topLevelApplication.parameters读取的时候只能读取第一个&符号前的地址。所以这个URL需要在Python代码里面进行URL encode,在Flex Application中URL decode。

    Python代码:

        def get (self):
            user = users.get_current_user()
            #Check login user
            if user:
                sign = urllib2.quote(users.create_logout_url('/openparty'))
            else:
                sign = urllib2.quote(users.create_login_url('/openparty'))
            #Init template vars
            template_values = {
                'sign' : sign,
            }

    Flex代码:

      var url : String = decodeURI(FlexGlobals.topLevelApplication.parameters['sign']);