About

<#TEMPLATE_INCLUDE_NINEPAGE_ABOUTME#>
  • Feb

    23

    很多时候我们需要用到设备的唯一识别码。安卓系统获取设备唯一识别码的资料网上比较多,但它们也并不通用,因为现在市场上的很多安卓机实际上它们的系统都是经过设备厂商修改过的,比如小米的系统,阿里云OS手机系统等等,所以实际上并不存在一个真正通用的方法。以下这个方法仅适用于 iPhone / iPad 设备或桌面设备,并不适用于 Android 系统(原因在后面)。

    苹果设备的 “IDFA、IDFV、UDID、OpenUDID 等等”这些标识区别可以在百度上找到,这些标识除了真正的 UDID 和 MAC 物理地址之外,其它的标识在很多情况下是会发生变化的:最常见的情况就是还原出厂设置后,会生成一个新的标识符。网络上一些第三方能够获得 UDID 的 SDK,实际上都是 OpenUDID,并不是真正的 UDID。

    苹果公司政策规定开发者是不能够获取用户设备的物理信息的(在 iOS 4.x 时代是获取 UDID 和 MAC 物理地址的,后来的新系统中都开始禁止了访问这些信息)。如果利用核客技术访问私有 API 被审核人员发现,那么一定会遭到苹果公司拒审;既便上架了被发现也会被下架。

    重要的事情说三边

    1、苹果公司政策规定不允许第三方开发者获取设备真正的唯一识别码!苹果公司政策规定不允许第三方开发者获取设备真正的唯一识别码!苹果公司政策规定不允许第三方开发者获取设备真正的唯一识别码!

    2、利用核客技术或系统漏洞调用苹果私有 API 会拒和下架!利用核客技术或系统漏洞调用苹果私有 API 会拒和下架!利用核客技术或系统漏洞调用苹果私有 API 会拒和下架!

    3、利用任何标识符统计用户数据必须接受苹果公司审核!利用任何标识符统计用户数据必须接受苹果公司审核!利用任何标识符统计用户数据必须接受苹果公司审核!

    4、开发者不要仅研究技术,也要知晓各平台的政策!开发者不要仅研究技术,也要知晓各平台的政策!开发者不要仅研究技术,也要知晓各平台的政策!

    技巧

    同一时间是指精确到毫秒级的时间!目录与创建时间!!硬件磁盘根目录即文件目录!!!

    从理论上来说,我们可以在同一时间创建两个目录,但那仅仅是理论上的,所以标题中“唯一”两个字加了引号。两块闪存盘同时间被格式化完成?这个概率可能比两个目录的创建时间完全相同的机率还要低。

    代码

    import flash.filesystem.File;
    import flash.globalization.DateTimeFormatter;

    trace("硬件信息:");

    var rootDirArr:Array = File.getRootDirectories();

    for (var i:uint = 0; i < rootDirArr.length; i++) 
    {
        var file:File = rootDirArr[i];

        var date:Date = file.creationDate;

        var dtf:DateTimeFormatter = new DateTimeFormatter(LocaleID.DEFAULT);
            dtf.setDateTimePattern("yyyy/MM/dd HH:mm:ss");

        var creationTime:String = dtf.format(date);

        //看到最后的这个 milliseconds 值了吗
        trace(file.nativePath + ":" + creationTime + "," + date.milliseconds);
    }

    如果是在我的 Windows 7 中调试运行,它会显示(它并不需要系统管理员的权限就能读取):

    硬件信息:
    C:\:2009/07/14 10:38:56,526
    D:\:2010/08/23 23:59:31,18
    E:\:2010/08/23 23:59:46,99

    如果是在我的 iOS 9.2 (iPhone6 Plus)中运行,它会显示(不需要越狱):

    硬件信息:
    /:2015/06/25 17:47:25,10

    使用这个方法最大好处就是不需要 ANE 文件。既便用户在系统设置中还原位置与隐私、还原广告标示符、抹掉所有内容与设置、升级系统等,磁盘的创建时间都不会发生变化,既便用户完全重置系统,还原所有设置到出厂状态,磁盘的创建时间都不会发生变化。经过测试,只有在使用线刷 / 强制刷机的情况下,磁盘才会被重新创建并且格式化,磁盘的创建时间才会发生变化 

    额外的话题:关于 Android 系统

    然后,以上这个方法并不适用于 Android 系统,在 Adobe 官方的 API  手册中 getRootDirectories() 方法下面已经有说明:“在根不可读的文件系统上,例如 Android 文件系统,返回的 File 对象的属性并不总是反映真实值。例如,在 Android 上,spaceAvailable 属性报告 0。” 安卓系统可以使用原生的方式获取设备id,制作成 ANE 文件给 AS3 调用。但正如前面第一段所述的,既便是 Google 提供的 getDeviceId() 这个 API 并不通用,因为国内很多手机厂商对安卓系统进行了修改,很多权限都发生了改变。

    比如,在不使用 ANE 的情况下,在未经修改的安卓系统中,可以用如下方法读取 MAC 网卡地址。我在 HTC 手机中进行了测试,-app.xml 中的权限:

    <!--删除 android.permission.INTERNET 权限将导致无法调试设备上的应用程序-->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!--应同时切换 ACCESS_NETWORK_STATE 和 ACCESS_WIFI_STATE 权限,才能使用 AIR 的 NetworkInfo API-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    AS3:

    var netWorkVec:Vector.<NetworkInterface > = NetworkInfo.networkInfo.findInterfaces();

    for (var i:* in netWorkVec)
    {
        if(netWorkVec[i].hardwareAddress)
            trace("MAC 物理地址:",netWorkVec[i].hardwareAddress);
    }

    结果输出了:

    MAC 物理地址: D8:B3:77:7F:00:47

    但是,同样的代码,在国内厂商经过修改后的系统里,它并不能正常输出 MAC 网卡地址,比如使用阿里云OS的联想手机经过测试就不能。所以如果 AS3 的安卓开发者们想要一个通用的获取设备硬件信息的方法,几乎是不可能。

    Oct

    31

    比较通用,反正 AS、JS 之类的都差不多语法。

    static public function randomAccountString(_len:uint):String
    {
        //帐号一般第一个字母不能为数字
        var charsForFirstChar:String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

        var firstChar:String = charsForFirstChar.charAt(uint(Math.random() * charsForFirstChar.length));

        var chars:String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        var randomChar:String = "";

        for (var i:Number = 0; i < _len - 1; i++)
        {
            randomChar += chars.charAt(uint(Math.random() * chars.length));
        }

        randomChar = firstChar + randomChar;

        return randomChar;
    }


    static public function randomPasswordString(_len:uint):String
    {
        //密码往往是区分大小写的,并且允许使用一些特殊符号
        var chars:String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,[]{}!@#$%&*()_-+=";

        var randomChar:String = "";

        for (var i:Number = 0; i < _len; i++)
        {
            randomChar += chars.charAt(uint(Math.random() * chars.length));
        }

        return randomChar;
    }

    Jun

    26

    继续《Adobe AIR 读取屏幕物理点数(非物理尺寸、非像素尺寸)的方法》,如果不使用 StageWebViewBridge-master,这里提供一个通过 StageWebView 获取 iOS 屏幕物理点数的样例代码(如果是安卓系统,可以直接用那篇文章中引用的官方推荐值 DPI 值 160)。

    JS 部份主要代码:

    package
    {
        public class ScreenJS
        {
            public function ScreenJS()
            {
                throw Error("无法实例化的类")
            }

            //这是一个奇怪的字符串写法
            //可以参考这个贴子:http://blog.zinewow.com/post/270.html
            //这样写的目的只是为了方便字符串在AS中换行
            static public const xmlString:String =

            (<![CDATA[
                <script type="text/javascript">

                    function getInfo() 
                    {
                        var u = navigator.userAgent;


                        if(u.indexOf('iPhone') > -1 || u.indexOf('iPad') > -1)
                        {
                            window.location.href = "http://127.0.0.1/?" + window.screen.height + "x" + window.screen.width;
                        }
                    }     

                    getInfo() ;

                </script>
            ]]>);
        }
    }

    AS 部份主要代码:

    //某个地方生成了 StageWebView 对象并添加了侦听器,一般就要主函数吧
    var view:StageWebView = new StageWebView();

    view.stage = stage;
    view.addEventListener(LocationChangeEvent.LOCATION_CHANGING, locationChaningHandler);
    view.loadString(ScreenJS.xmlString);


    //侦听器函数
    private function locationChaningHandler(e:LocationChangeEvent):void
    {
          e.preventDefault();//我们并不是为了跳转网页,所以这里中止跳转

          var location:String = e.location;
          var dpi:String= String(location.split("?")[1]);
          trace("dpi:", dpi);
    }

    Mar

    12

    MinimalComps 带 Tree 组件

    • 0 Comments
    • Flash Platform

    MinimalComps 因为它的轻量级一直比较讨人喜欢,但它并没有内置的树组件。在 GitHub 上找到了一个带有 Tree 组件的版本。

    下载地址: minimalcomps-master.zip

    配置数据:

    //数据源配置
    var treeItems:Array = [
      { label:'treelist_test_app', items:[
        { label:'bin', expanded:false, items:[
          { label:'js', items:[
            { label:'swfobject.js' }
          ] },
          { label:'expressInstall.swf' },
          { label:'index.html' },
          { label:'TreeListTestApp.swf' }
        ] },
        { label:'lib', items:[] },
        { label:'obj' , items:[
          { label:'TreeListTestAppConfig.old' },
          { label:'TreeListTestAppConfig.xml' }
        ] },
        { label:'src', items:[] },
        { label:'file_tree_test.as' },
        { label:'TreeList Test App.as3proj' },
      ] }
    ];

    创建组件:

    //创建树组件
    testTree = new TreeList(mainContainer , 00, treeItems);
    testTree.setSize(200200);
    testTree.addEventListener(Event.SELECT, handleTestTreeSelect);

     侦听事件: 

    private function handleTestTreeSelect(e:Event):void
    {
      trace('TreeList select:', TreeList(e.target).selectedItem.label);
    }

    GitHub 链接:https://github.com/AdamHarte/minimalcomps

    原文链接:http://blog.adamharte.com/added-treelist-to-minimalcomps/

    Feb

    4

    Starling 粒子系统

    • 0 Comments
    • Flash Platform

    Dec

    25

    原文地址:http://www.rubenswieringa.com/blog/flex-book-component-beta

    原博客主好几年都未更新博客了,将来也许有一天会关闭博客,我先转载过来收藏一下,个人觉的这份 AS3 版本的 Flex 翻页源码非常棒。

    它的主要功能包括:翻页效果、自定义硬翻页或软翻页效果、支持透明页面、锁定页面、撕页效果等等(反正它的原文中都介绍了并且提供了 DEMO 效果)。其中撕页效果非常炫,它能将一个页面像一张纸一样从书籍中撕下来。

    我的翻页日记本程序《Dear Diary - 桫椤札记》用的就是这个翻页引擎。

    源码下载: 翻页效果源码demo.zip

    API 手册:

    http://www.rubenswieringa.com/code/as3/flex/Book/docs/

    http://www.cs.vu.nl/~eliens/assets/apidoc/book/

    More...

    Nov

    20

    AS3 单例模式

    • 0 Comments
    • Flash Platform
    package
    {
        public class Instance
        {
            //是否已经有单例对象
            static private var _hasInstance:Boolean = true;

            //单例
            static private var _instance:Instance;

            public function Instance()
            {
                if(_hasInstance)
                {
                    throw new Error("这是一个单例,须通过 getInstance() 获取实例");
                }
            }

            static public function getInstance():Instance
            {
                if (!_instance)
                {
                    _hasInstance=false;
                    _instance = new Instance();
                    _hasInstance=true;
                }
                return _instance;
            }
        }
    }

    Oct

    21

    Oct

    12

    一个控制声音的类:SoundAS

    • 0 Comments
    • Flash Platform

    在绝大多数情况下我们只是需要简单的控制声音的加载、播放、暂停、淡入淡出等,这个足够用了,可以免去 Flash 内置声音控制相关的类业务太繁杂的过程。

    https://github.com/treefortress/SoundAS

    Aug

    18

    package {

        import mx.validators.ValidationResult;
        import mx.validators.Validator;

        public class MatchValidator extends Validator{
            private var _matchSource: Object = null;
            private var _matchProperty: String = null;
            private var _noMatchError: String = "Fields did not match";

            [Inspectable(category="General", defaultValue="Fields did not match")]
            public function set noMatchError( argError:String):void{
                _noMatchError = argError;
            }
            public function get noMatchError():String{
                return _noMatchError;
            }

            [Inspectable(category="General", defaultValue="null")]
            public function set matchSource( argObject:Object):void{
                _matchSource = argObject;
            }
            public function get matchSource():Object{
                return _matchSource;
            }

            [Inspectable(category="General", defaultValue="null")]
            public function set matchProperty( argProperty:String):void{
                _matchProperty = argProperty;
            }
            public function get matchProperty():String{
                return _matchProperty;
            }

            override protected function doValidation(value:Object):Array {

                var results:Array = super.doValidation(value.ours);

                var val:String = value.ours ? String(value.ours) : "";
                if (results.length > 0 || ((val.length == 0) && !required)){
                    return results;
                }else{
                    if(val != value.toMatch){
                        results.length = 0;
                        results.push( new ValidationResult(true,null,"mismatch",_noMatchError));
                        return results;
                    }else{
                        return results;
                    }
                }
            }  

            override protected function getValueFromSource():Object {
                var value:Object = {};

                value.ours = super.getValueFromSource();

                if (_matchSource && _matchProperty){
                    value.toMatch = _matchSource[_matchProperty];
                }else{
                    value.toMatch = null;
                }
                return value;
            }
        }
    }