Pages:  1 2 May 2010 – lytsing's Blog

Month: May 2010

  • Android Market的 Loading效果

    在Android中,要实现Loading效果,一般情况下都使用ProgressDialog控件。ApiDemos/src/com/example/android/apis/view/ProgressBar3.java 提供两个demo:
    Progressbar show Indeterminate
    progressbar show Indeterminate No Title
    仔细看了Android Market,发现却是不一样的,请看截图:
    那到底如何实现呢?首先,我们创建一个布局文件,
    res/layout/fullscreen_loading_indicator.xml, 其内容如下:
    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:gravity="center_vertical|center_horizontal"
        android:orientation="horizontal"
        android:id="@+id/fullscreen_loading_indicator"
        android:visibility="gone"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <ProgressBar
            android:layout_gravity="center_vertical"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="?android:attr/progressBarStyleSmall"
            >
        </ProgressBar>
        <TextView
            android:id="@+id/current_action"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5.0dip"
            android:text="@string/loading"
            >
        </TextView>
    </LinearLayout>
    
    然后在main.xml 把它include 进来
    
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <LinearLayout
            android:orientation="vertical"
            android:id="@+id/main_info"
            android:visibility="gone"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            >
        </LinearLayout>
        <include
            android:visibility="visible"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            layout="@layout/fullscreen_loading_indicator"
            >
        </include>
    </FrameLayout>
    
    主程序 Loading.java:
    package org.lytsing.android.loading;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.LinearLayout;
    
    public class Loading extends Activity {
    
        private LinearLayout mLoadingLayout;
    
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            // TODO: dismiss the loading, use this snippet code.
            //mLoadingLayout = (LinearLayout)findViewById(R.id.fullscreen_loading_indicator);
            //mLoadingLayout.setVisibility(View.GONE);
        }
    }
    运行的效果为:
  • Android 代码风格指南

    翻译状态: 完成进度82%

    本文的英文原始文档(Android Code Style Guidelines for Contributors)地址 : http://source.android.com/source/code-style.html

    国内用户如果无法直接访问,请自行翻墙。第一次翻译Java类的文档, 如果有差错和不妥之处, 请不吝指出.

    Android的代码风格规则

    The rules below are not guidelines or recommendations, but strict rules. Android代码贡献者,如果他们不遵守这些规则,那么一般他们的代码不会被接受
    并非所有现存的代码遵循这些规则,但所有的新代码希望能如此

    Java 语言规则

    我们遵循标准的Java编码约定。
    我们还添加一些规则:
    1. 异常:不要在没有说明的情况下捕捉和忽略它们。
    2. Exceptions : 不要捕捉一般异常, except in library code at the root of the stack.
    3. Finalizers :一般不使用它们。
    4. Imports : 完全符合引入

    Java库规则

    这里有一些关于使用android Java库与工具的约定。在某些情况下,该约定在一些重要的方式已经发生了变化,老的代码可能在使用过时的模式或库。当使用这些代码,最好继续保持与已存在的风格一致(参见一致性)当创建新的组件从来不使用废弃库。

    Java的风格规则

    当所有文件都保持一致的风格时,程序就更容易维护。我们遵循标准的Java编码风格,他们由Sun公司为Java编程语言制定的编码约定,除少数例外,和增加一些。这些风格指南是全面而详细的,在Java社区很常用。

    此外,我们执行下面的样式规则:
    1. 注释/Javadoc :使用标准样式写它
    2. 简短的方法 :不写超大的方法
    3. 字段(成员变量):要么是在该文件的顶部,或紧接在使用它们的方法前面。
    4. 局部变量 :限制作用域
    5. 引入 :android;第三方按字母顺序排列;java(x)
    6. 缩进排版 :4个空格,没有制表符(tab)
    7. 行长度 :100个字符
    8. 字段命名 : 非公有的,非静态字段以m开头,静态变量以s开头 。
    9. 括号 :开括号不要独占一行
    10. 注解 :使用标准的注解。
    11. Acronyms are words : Treat acronyms as words in names, yielding XmlHttpRequest , getUrl() , etc.
    12. TODO的风格 :“TODO:在这里写描述”
    13. 一致性 :看看你周围的
    14. 日志记录 :小心日志记录,它开销很大。

    Javatests样式规则

    1. 测试方法的命名 :testMethod_specificCase是正确的
  • 用C++写ruby扩展

    为了测试CDMA短信的发送方便,于是想到编译成lib,提供ruby使用。

    SmsObject.h:
    void SendSms(const char* msg, const char* number, char* out);

    main.cpp:

    //
    // by deli 2009.6.11
    //
    #include <ruby.h>
    #include "SmsObject.h"
    
    static VALUE makepdu(VALUE self, VALUE arg1, VALUE arg2) {
        VALUE s;
        char pdu[512] = {0};
        char* msg = RSTRING(arg1)->ptr;
        char* number = RSTRING(arg2)->ptr;
    
        SendSms(msg, number, pdu);
        s = rb_str_new2(pdu);
    
        return s;
    }
    
    extern "C"
    void __declspec(dllexport) Init_sms() {
        VALUE myModule = rb_define_module("CDMA");
    
        VALUE myClass = 
            rb_define_class_under(myModule, "SMS", rb_cObject);
    
        int arg_count = 2;
        rb_define_method(myClass, "makepdu", RUBY_METHOD_FUNC(makepdu), arg_count);
    }

    注意: C++要加上RUBY_METHOD_FUNC, C不用加。不然就会出现类似的错误:
    error C2664: ‘rb_define_method’ : cannot convert parameter 3 from
    ‘unsigned long (unsigned long,unsigned long,unsigned long)’ to ‘unsigned long (_
    _cdecl *)(…)’
    None of the functions with this name in scope match the target type

    test.rb:

    #!/usr/bin/ruby 
    
    require 'sms'
    
    include CDMA
    
    obj = SMS.new
    pdu = obj.makepdu('什么都可以想,什么都可以不想,便觉得是个自由的人', '15338896034')
    
    puts pdu

    D:\Ruby\ruby-serial\sms>ruby test.rb
    0000021002040702c54ce225a8d008420003200000013220c27602724487ea9f7a772b079ff86276
    02724487ea9f7a772a706b079ff8627dfc4e4afcbb317a71540f53a98bb42275d00501a70801c00d
    0101

    大概就是这样。

  • 用gsmmux 测试via cdma多路复用功能

    现在杭州的威盛已实现了GSM协议07.10 multiplexer。
    gsmmux 可以在 developer.berlios.de/projects/gsmmux/ 上获得.按照说明安装就可以了。
    代码默认用的是AT+CMUX开启功能,而CMUX在CDMA另有别用,所以他们就用VMUX来替代,在代码里,把CMUX改为VMUX,重新编译。

    运行 mux,得到两个虚拟逻辑串口 /dev/mux0 /dev/mux1,
    开一个终端1 cat /dev/mux0 观察数据
    再开一个终端2输入 echo -e “AT\r\n” > /dev/mux0
    终端1有 “OK” 响应就行了。
    同样的方法测试 mux1.

    核心代码在 gsm0710.c

    简单说明一下。
    1190 行的 main函数,读取命令行参数,
    1272 行 daemonize(_debug) 设置为unix下经典后台程序,下来是设置信号中断机制。

    openDevicesAndMuxMode 函数 打开modem 初始化,发 “AT+VMUX=0” 进入mux模式。

    MUX启动过程
    主机发: AT+VMUX=0
    模块回复:OK /*进入MUX模式*/
    主机发: F9033F011CF9 /*建立DLC0*/
    模块回复:F9037301D7F9
    主机发: F9073F01DEF9 /*建立DLC1*/
    模块回复:F907730115F9
    F901EF09E305070D9AF9 /*DLC1 MSC 命令*/
    主机发: F90B3F0159F9 /*建立DLC2*/
    模块回复:F90B730192F9
    F901EF09E3050B0D9AF9 /*DLC2 MSC 命令*/

    /*…开始进行MUX协议的数据传输…*/

    在 1106行的openDevicesAndMuxMode,
    三次(一般是三次,可以设置)打开 /dev/ptmx ,得到三对主从终端,通过符号链接,创建两个虚拟串口

    /dev/mux0 /dev/mux1 这时就可以像正常访问串口一样访问它们,一般 /dev/mux0
    用来专门发AT, /dev/mux1 用来发送数据业务,比如pppd 拨号上网。

    数据流大概是这样的
    /dev/ttyS0 <---> | /dev/pmux <----> /dev/mux0 | <----> at command
    /dev/ttyS0 <---> | /dev/pmux <----> /dev/mux1 | <----> cdma pppd

    下面
    1162行 – 1166行
    for (i = 1; i <= numOfPorts; i++)
    {
    sleep(1);
    write_frame(i, NULL, 0, SABM | PF);
    syslog(LOG_INFO, "Connecting %s to virtual channel %d on %s\n", ptsname(mux_fd[i-1]), i, serportdev);
    }

    write_frame(i, NULL, 0, SABM | PF); 实现的就是发
    F9033F011CF9 /*建立DLC0*/
    F9073F01DEF9 /*建立DLC1*/
    F90B3F0159F9 /*建立DLC2*/

    然后进入 while循环,用selelct 实现 i/o 多路复用,
    (1)检查物理串口,如果有可读的数据,读取放到buffer,然后解析数据帧,并发送到虚拟逻辑串口
    (2)检查虚拟逻辑串口,如果有可读的数据,构造数据帧,写入物理串口。

    就这样,基本原理是数据分组打包与解包。

  • 在MTK平台上部署CDMA

    原文在 2009/10/14 发表于broncho论坛,转载请写出处,谢谢!

    在MTK平台上部署CDMA

    [注] 我们用的MTK版本是6225 ,CDMA 模组是威盛via的。此项目没有量产,代码放在那也是加密冷藏没有价值,写思路出来与大家讨论关于程序设计。本人不是MTK专家,有分析不对之处,请指出,谢谢。

    [硬件篇]
    加一个cdma模组,具体操作本人不知道,就不讲述了。

    [软件篇]
    让我们来分析一下。既然在MTK平台,上层应用最好不要改动,就只能在底层修改。在L4层我们发现,MTK提供了mmi接口,那么我们就有机会重写这些函数,这个就是最大前提。对于CDMA,要对AT做封装,适配于L4(适配器设计模式)。我们可以开一个task,专门处理CDMA AT,串口数据的读取。考虑到CDMA的AT比较弱,特别是三方通话,呼叫保持,对AT的处理,得用较多全局变量来保存上下文。

    代码目录:

    代码: 全选
    Vendor -|
    |- ATCMD -| at_cdma
    | at_gsm
    | at_l4a
    | at_thread

    At_thread 主要是处理AT的线程.
    首先创建一个task.
    /* task */
    void AT_Cmd_main(task_entry_struct * task_entry_ptr); 一个while 循环,处理消息。

    At_gsm 是一个参考物,为了测试cdma方便,我们自己实现MTK的AT封装。At_cdma 是cdma编解码目录。可以说,at_cdma继承于at_gsm,两者有统一的接口(主要是gsm标准,cdma适应于gsm),所以很多的工作,在at_cdma做了很大的调动。如果实现和gsm不一样,我们就要rewrite接口实现,基于函数替换。

    上面已有提到过,我们要重新实现mtk提供的l4a层,那么at_l4a 就是干这样的活了。为了不直接在mtk l4a的函数里修改,我们采用include 的方式。所有的处理函数放在AT_L4c_Funs_V07B.c
    写我们自己的 l4a_callback_ex.c ,在ps\l4a\src\l4a_callback.c 的末尾 #include “l4a_callback_ex.c” ,然后在 l4a_main.c的 l4a_recvmsg(ilm_struct *ilm_ptr)里加以区别:如果 l4c_current_mod_id == l4c_atcmd_mod_id,就调用我们实现的接口函数处理cdma,否则,就调用mtk提供的处理gsm(不变)。

    串口通讯的处理
    利用w32_uart.c 里提供的一些函数,打开串口,设置波特率,数据控制流等参数。
    对数据的发送,接收,加入休眠模式,以免功耗过大。

    AT的处理
    在MTK上对CDMA AT的处理,则采用赤裸裸的封装,源于有开发过Firebird BBS的经历,我更喜欢这种最原始的美,不要和我谈什么软件架构,设计模式的,考虑越多,项目越难以进展,领导只关心是否能跑起来,不然项目取消,大家散伙吧。说远了。AT具有随意性,我们假设发出请求的AT,就有相应的响应,设一个定时器,来处理超时问题。如果超时了,我们再发AT,如果有OK回应,就说明模组还能正常工作,那么这个AT的请求就是失败的。对自动上报的消息的处理,在获取AT响应时,我们判断是自动上报的消息,则当场处理,继续读下一行数据,直到有期望的响应值。如果请求发出一个AT, 既有自动上报消息,又有自身的请求响应,该如何处理呢?举个例子,查询网络注册情况,发出AT+CREG?后,
    +CREG:1,0×3614,0×2

    +CREG:1,0×3614,0×2

    +CREG: 2,1,0x00C3,0x00AD

    我们发现,请求的响应有4个参数,而自动上报的有三个参数。再举个例子:
    开机时,模块会自动开启一个15min的计时器,如果15分钟内没有收到AT+LCT指令,模块就自动关机,如果收到了该指令,15min的计时器又重新开始计时。所以我们看到+LCT主动上报时,就发出AT+LCT。问题来了,我们打电话时,查询CLCC,那么问题出现如下:
    AT+CLCC?
    +LCT
    AT+LCT
    +CLCC:1,0,1
    ok
    ok

    两个响应消息交错在一起,就难以分析了,我们就得保证消息串化。
    等等。AT响应的处理,大部分工作是解析字符串,取出你想得到的数据,填充结构体。总之,在做AT处理时,要反复测试,考虑很多的异常,这便是软件设计的难点之一。

    AT层上GSM 与CDMA的差异
    就列举几个比较重要的差别,很多小细节还是非常多的,请参阅厂家的AT SPEC。
    (1)AT测试命令,比如信号查询 GSM是AT+CSQ;CDMA则是 AT+CSQ? 很多种情况如此,CDMA在后面要加上一个“?”号。
    (2)AT响应字符串
    比如, GSM:
    +CCLK: “04/01/19,15:38:32″
    +CCLK:后面有空格
    CDMA:
    +CCLK:2008/8/6,13:57:5,3
    +CCLK: 后面没有空格
    响应字符串解析函数必须能处理空格问题。可以写一个类似于sscanf的函数,专用解析AT响应字符串。
    (3) CDMA中”UIM”的字符串,这个主要出现在电话本与短信。MTK上是”SIM” 对应 0, “ME”对应1;CDMA把”UIM”对应为0即可。此外,短信与电话本,gsm从1开始读取,cdma则从0开始读。
    (4) TE特征字符串设定
    GSM: AT+CSCS
    CDMA没有这方面的设定,默认为UNICODE,在电话本处理中,AT请求和响应要特别指出.
    (5) 短信的PDU编解码完全不同.请参考 GSM0707与3gpp2 的C.S0015-B
    (6) 电话状态查询CLCC,CDMA提供的功能很弱,不管有没有来电,拨出电话,查询CLCC总有一个+CLCC:响应,GSM则不然。CDMA处于三方通话,呼叫保持,呼叫等待时,查询CLCC也是只有一路+CLCC:响应,这样,我们得自己构建适应于GSM的CLCC,维护,更新CLCC列表。

    开关机流程
    (1)开机
    +VPUP
    模块上报开机启动标志,也可根据该命令判断模块重启,收到该命令后进行初始化设置. (模组有问题时,这个上报是不可靠的.)
    +MSStatus:0
    模块上报协议栈已打开。
    +VROM:1
    模块上报漫游状态(1 为非漫游状态)。 这个上报是不可靠的,就是说,并不是每次开机都会有这个上报。不过,如果出现这个,我们认为可以显示中国电信了,其他手机已开机就显示运营商,估计也是照这个来做。

    AT+ISF?
    查询模块初始化状态,2s~3s 执行一次
    +ISF:1
    模块初始化完成。

    接下来才可以对电话本,短信,通话记录初始化。

    由上,在+MSStatus:0与 +ISF:1之间,可以操作的行为有:
    (0)ATE0 软件已经默认关闭回显,该步骤可省。
    (1)注册网络与信号上报. AT+CREG=2 AT+ARSI=1
    (2)查询UIM是否插好?AT+CPIN?
    (3)查询信号 AT+CSQ?
    (4)查询国家码,网络码 AT+VMCC? AT+VMNC?
    (5)选择语音通道 AT+SPEAKER=0

    开机流程比较繁琐,要适应与MTK的启动流程,比如开启Pin码,手机密码,网络运营商的获取,信号强度上报等。一般情况下,模块加电后会自行开启协议栈。不插UIM卡启动,模块不会自行打开协议栈。需要输入PIN的情况下,只有在用户输入正确的PIN,模块才会开启协议栈。在以上任何情形下开机,模块都会上报(+VPUP),客户可以根据此上报来判断正常使用中的模块是否重启。
    (2)关机:
    由于关机需要在网络上进行一些登记操作,因此正常的关机步骤是建议+CPOF关闭协议栈后延迟一段时间断电。
    (3)飞行模式:
    模块实现飞行模式可以直接使用+CPOF关闭协议栈,+CPON打开协议栈返回正常模式。
    查询当前是否在飞行模式:+VPON.

    细节:
    长短信的发送:
    Cdma不能无缝的发送长短信,一般要等到 +CDS:上报才能发出下一条,一般情况下,发出一条短信过4-5秒后就有+CDS上报,不过,如果发给自己的话,这个时间会更长,我们采用延时30秒。一般如果发多条短信,第二条一般会返回+CDS:2,2,66 ,就是说,发不成功。我们要把它保存起来,重发一次。

    结尾
    当然,要做到符合最新电信需求规范,就比较困难了。MTK本是在GSM协议上做起来的,GSM于CDMA本来就存在很大的差别。说不定过不久MTK会发布C+G的版本,啥东西人家都帮你弄好了,就等着只改界面换个图片铃声吧。