Tag Archives: android app

Android上的psf播放器 MinePsfPlayer

很久以前就想在Android上做一个psf的播放器,耽搁了许久,现在总算搞出了第一个版本。

稍微介绍一下历史:
1) 最早是基于sexypsf library (http://projects.raphnet.net/#sexypsf),在PSP上写了一个能播放psf音乐的demo;不过后来不知道什么原因,没有继续做下去;
2) 后来发现了Audacious,一个从XMMS派生出来的开源播放器,里面有psf plugin,从code里看,psf的解码也是基于sexypsf的,但里面还包含了psf2的lib,在linux上工作良好;
3) 于是用了这个lib,在SDL库的基础上写了一个demo,可以在Windows/Linux上可以播放psf的demo;
4) Android出来之后,就想在上面写个psf播放器,不过刚开始没有NDK,感觉搞不定;
5) NDK出来之后,终于可以在android上写一个播放psf的demo了,在模拟器上跑,效果惨不忍睹,解码得太慢了;当时还没有Android手机,以为性能会有问题,作罢;
6) 买了Milestone之后,在实际的手机上跑了一下,发现性能上基本上没问题,但是有各种问题(实际上是code里的bug),当时在搞短信震动的app,psf的播放又往后拖了很久;
7) 现在终于搞定了psf的播放器!(在手机上听各PS经典游戏的原声,内牛满面啊)

TODO: 最高优先级的当然是PSF2的播放,这样就可以在手机上听FFX的音乐了,但愿不会拖太久。。。

总之,欢迎下载~
https://play.google.com/store/apps/details?id=com.mine.psfplayer
MinePsfPlayer_GooglePlay

Share

[Android Dev] Gmail OAuth (2)

这一部分主要介绍如何使用已获得的Access Token去获取Gmail以及计算未读邮件的数量。

1. 从上一篇博客里获得的AccessToken以及Secret,生成OAuthConsumer,非常简单。

CommonsHttpOAuthConsumer consumer =
  new CommonsHttpOAuthConsumer("anonymous", "anonymous");
consumer.setTokenWithSecret(token, secret);

2. Gmail提供了RSS Feed,只需要使用token取得RSS Feed,就拿到了Gmail的内容。其中:
https://mail.google.com/mail/feed/atom/ 对应Inbox里的未读邮件
https://mail.google.com/mail/feed/atom/unread/ 对应所有的未读邮件
https://mail.google.com/mail/feed/atom/labelname/ 对应某个label的未读邮件
我只需要取得用户的Inbox的未读邮件,因此code是这样的:

HttpGet request = new HttpGet("https://mail.google.com/mail/feed/atom/");
// sign the request
try {
  consumer.sign(request);
} catch (Exception e) {
  e.printStackTrace();
}
// send the request
HttpClient httpClient = new DefaultHttpClient();
org.apache.http.HttpResponse response;
try {
  response = httpClient.execute(request);
  feedString = read(response.getEntity().getContent());
} catch (Exception e) {
  e.printStackTrace();
}

feedString里就保存了Google返回的feed的内容。而函数read是为了把response里的内容读出来生成String. 这个是从网上抄来的…

private static String read(InputStream in) throws IOException {
  StringBuilder sb = new StringBuilder();
  BufferedReader r = new BufferedReader(new InputStreamReader(in), 1000);
  for (String line = r.readLine(); line != null; line = r.readLine()) {
    sb.append(line);
  }
  in.close();
  return sb.toString();
}

3. 有了feed,就剩下计算unread count的工作了。这分成两部分。
i) 从String生成XML Document,其实是一个DOM tree;
ii) 在XML Document里parse数据,取得unread count.
关于i) 也是网上有相应的code:

private static Document XMLfromString(String xml){
  Document doc = null;
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  try {
    DocumentBuilder db = dbf.newDocumentBuilder();
    InputSource is = new InputSource();
    is.setCharacterStream(new StringReader(xml));
    doc = db.parse(is);
  } catch (ParserConfigurationException e) {
  // XML parse error
  return null;
  } catch (SAXException e) {
  // Wrong XML file structure
  return null;
  } catch (IOException e) {
  //I/O exeption
  return null;
  }
  return doc;
}

关于ii) Gmail的一个个邮件是放在一个个entry里的,所以只需要parse entry就行了。

NodeList nodes = feedDoc.getElementsByTagName("entry");
unread_count = nodes.getLength();

这就搞定了未读邮件数量的计算。

4. 虽然正常情况下都没问题,但是假如用户改了密码,token就无效了,应该检测这种情况,然后让用户重新进行authenticate。虽然这不常见,但也得处理。
假如Token变得无效,Gmail会返回一个显示Unauthorized的网页。因此最简单的方法是通过字符串判断. 注意如果是正常的feed,string是以<?xml为开头的,而如果是unauthorized的网页,就以<HTML>开头。

private static boolean verifyValid(String doc) {
  return !(doc.startsWith("<HTML>") && doc.contains("<TITLE>Unauthorized"));
}

这样就解决了。

Q.E.D.

Share

[Android Dev] Gmail OAuth

自从几个月前Gmail更新之后,第三方app就不能再通过ContentResolver去获取gmail的内容和状态了。因此很多Gmail Unread/Reminder就没办法用以前的方式工作了。我的app里Gmail Reminder的功能也因此暂时取消。
最近终于有时间继续写code,google了一番之后发现目前最可靠的方法就是用OAuth来访问Gmail。

不过在Android实现OAuth还是有一点tricky的,在这边写下来,作为开发笔记,说不定也能帮到一些人 🙂
p.s. 得感谢Google Search和Stackoverflow,从里面抄了不少code.

计划分成2部分: 1) OAuth认证; 2) 通过Token访问Gmail并计算unread count
具体的细节可以直接访问开源项目 https://code.google.com/p/minemessagevibrator/

一)OAuth: 主要使用signpost库实现。(https://code.google.com/p/oauth-signpost/)

1. 是一个处理OAuth以及callback的Activity. 这个Activity负责打开oauth的网站,让用户认证完成之后得到callback,并得到和保存token.
说明:
i) android:scheme和android:host是为了回调的时候响应自定义的URI: “mine-activity://mine-vibration/”
ii) singleTask是为了回调的时候能得到onNewIntent()消息

<activity android:name=".oauth.MineOAuthAccessActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mine-activity" android:host="mine-vibration"/>
</intent-filter>
</activity>

2. 准备signpost的OAuthProvider, 其中consumerKey和consumerSecret是应用的key和secret,熟悉Twitter OAuth的人肯定知道。在Google上可以用”anonymous”

mConsumer = new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
mProvider = new CommonsHttpOAuthProvider(
"https://www.google.com/accounts/OAuthGetRequestToken?scope="
+ URLEncoder.encode("https://mail.google.com/", "utf-8"),
"https://www.google.com/accounts/OAuthGetAccessToken",
"https://www.google.com/accounts/OAuthAuthorizeToken?hd=default");
mCallbackUrl = "mine-activity://mine-vibration/";

3. 获得request token的URL,很简单。但是注意这一步需要网络连接。

String authUrl = mProvider.retrieveRequestToken(mConsumer, mCallbackUrl);

4. 打开浏览器让用户进行认证,也很简单。

startActivity(new Intent("android.intent.action.VIEW", Uri.parse(ret)).
setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
Intent.FLAG_ACTIVITY_NO_HISTORY |
Intent.FLAG_FROM_BACKGROUND));

5. 当用户认证完,网站会重定向到callback URL,就是 “mine-activity://mine-vibration/”,在Android里,会启动MineOAuthAccessActivity并调用onNewIntent(),我们要做的事情是从回调的参数里获得verifier,并获取access token & secret。这样OAuth的认证过程就结束了。之后app可以通过access token去访问gmail。

Uri uri = intent.getData();
String verifier = uri.getQueryParameter("oauth_verifier");
mProvider.retrieveAccessToken(mConsumer, verifier);
mConsumer.getToken();    // AccessToken
mConsumer.getTokenSecret();    // Token Secret

6. 在小内存的机器上(比如说俺的Milestone),启动浏览器之后基本上原来的Activity就会被kill掉,而且process也很可能被杀掉,这样当callback回来的时候,会发现原来的变量都没了。所以我们必须保存OAuth相关的变量,这样可以在启动浏览器之前保存,在重启Activity之后恢复。可以通过java的Serialize object来实现。
保存:

FileOutputStream fos = context.openFileOutput("xxx", // saved file name
Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(xxx);    // the object to save
os.close();

读取:

FileInputStream fis;
fis = context.openFileInput("xxx");  // the file name
ObjectInputStream is = new ObjectInputStream(fis);
xxx= (xxx) is.readObject();    // the object to load
is.close();

但是signpost的类虽然是Serializable的,但是其中有个不知道算不算bug的问题,其中CommonsHttpOAuthProvider里的httpClient被声明成transient的,所以这个变量是不能被serialize的。因此得额外加一句:

mProvider.setHttpClient(new DefaultHttpClient());

Done!

btw, 通过Token访问Gmail并计算unread count会有另外一篇blog来介绍。

Share

我的2010

新的2011已经过了一小段了,总结一下过去的2010,感慨万千啊…
  1月,在东北,长白山顶,看到了天池;
  2月,过年,下定决心,这一年要好好过,好好努力;
  3月,没什么特别的,似乎还在纠结中;参加了一次科学松鼠会的活动,王道怀的关于人类的讨论,很不错,当时报名还是挺容易的,一下子我和两个同事都报上名了;后来的松鼠会活动,就再也没报上名过…
  4月,开始忙碌起来了,公司的五子棋比赛得了第一名,要代表上海Moto去杭州参加Moto China的比赛;为了去看话剧《我爱桃花》,错过了一次高中同学的聚会,不过也值得,因为话剧真的很不错,有一点点感动;之后又有公司的春游,第二次去了黄山,见到了一览无余的黄山,顺便去了西递,一个古镇,让我想起了玉祁镇;
  5月,和朋友去了厦门、福建玩,鼓浪屿的风情、土楼的怀旧,让我难以忘怀,也从这时开始,我每次出去玩,都会寄明信片了(受某人影响,呵呵);之后又去了杭州比赛,第一轮即惨败,就当是去杭州玩一趟,顺便去了西溪湿地,蛮不错的地方,对我来说,它因为《非诚勿扰》而出名,也让我想起往事,不堪回首;月底去了一次世博会,建筑、夜景确实不错,只是人多就是个杯具;
  6月,好消息,毛桑跳槽Qualcomm(这其实跟我没撒关系,不过也是俺舍友嘛…),因为公司在南京西路和静安寺中间,于是他成为了南西男;而老板也许由于某些因素,给我offer了瑞典的travel的机会;
  7月,世界杯月,宇宙队班-梅西+卡西的西班牙拿了冠军;这个月里更重要的一件事是,与Fredrik的一段聊天,让我改变了关于政治、自由的看法。我说,我不喜欢、不关心政治,只喜欢自由;他说,人怎么可能不关心政治呢,每个都活在政治里,自由就是一种政治。回过头想想,如果自己不闻不问,就是被国内的政治操控着,不如自己为了自己的目标尝试改变些什么——微博是方式之一;去听了“交响情人梦”的音乐会,回顾着Nodame的剧情,听着经典的乐曲,很享受;
  8月,第一次出国,很happy的瑞典行。从上海的夏天到Linkoping的夏天,感觉这是两个季节。北欧宜人的气候优美的风景迷人的建筑,我享受、羡慕在那儿的生活——9点上班,5点下班,之后是自己的生活,逛街、骑车、打球、攀岩、露天电影、教堂的管风琴音乐会、等等,丰富多彩。趁着一个周末坐火车去了Stockholm,参观了诺贝尔颁奖晚宴的举行地City Hall,这样的旅行感觉超赞;
  9月,公司分拆,俺们发了package,于是我跑去南京把房贷还完了,当天往返的高铁,确实很快。在自己的手机上写了个小程序MineMessageVibrator(Mine短信震动切换), 一开始只实现widget切换震动的功能,后来加了未读短信提醒,感觉很实用。放到了Google Android Market上免费下载,这是我的第一款android market app;
  10月,发现我的app下载量接近5000,很得意;有一些用户发信要求一些新feature,于是开始按照用户的需求修改、增添功能,继续完善它;老家的前洲中心小学百年校庆,我妈不是校,居然也参加表演去了,呵呵;
  11月,发现还有9天年假不用就浪费,于是请了7天假,加上两个周末,去四川玩了11天,第一次去了高原,又认识了新的朋友,又是一次很赞的旅行!
  12月,在夏天之前就开始和同事一起游泳,当然对我来说只是学游泳,夏天人多暂停了,秋天之后还是每周一次的游泳,到了年底,我终于学会了,哈哈~ 我的app评价不错,日均150~200的下载,总的下载量超过了10000,成为了还算热门的程序;我决定继续完善它。
  另外,跟着我的同事、朋友们,受他们影响,还有了这样的规律生活:每周一打羽毛球,每周三或者周四游泳,每一到两周跑一次步,偶尔的周末也会去打一场羽毛球,更偶尔的周六会去听“东方市民音乐会”。我很享受这样的work & life balance.
  我的2010还是蛮充实的,出去玩了不少,见识了很多新东西,认识了新朋友,很不错,我要赞一下我的2010!

Share