MEDIASでperlプログラミング

はじめに

iPhone 3Gを使っているんですが,docomoのMEDIASを使うことになりました.元々別の機種に変えようとは思っていたんですが,訳あって2台持ちとなっています.そのうちiPhoneは解約しますけど.んで,せっかくAndroid機を手に入れたのでsl4aを入れてperlでプログラミングしようと思い立ちました.この記事はサンプルスクリプトを動かすまでについて書いています.

Macからのadb

sl4aを使うだけなのでAndroid SDKをすべてインストールする必要はありません.adbがあればいいだけなのですが,ここではまりました.Macからusbを使ってadbでMEDIASを制御するにはMEDIASのベンダIDが必要で,これを~/.android/adb_usb.iniに記載しておく必要があったのです.ちなみに,adb_usb.iniファイルは

$ android update adb

を実行すると作成されます.ここにMEDIAS用の0x0409を記入します.こんな感じ.

# ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT.
# USE 'android update adb' TO GENERATE.
# 1 USB VENDOR ID PER LINE.
0x0409

これで

$ adb devices
List of devices attached 
xxxxxxxxxxxxxxx	device

(xxxの場所は数字が入ります)と出力されてMEDIASへのアクセスができるようになりました.注意しないといけないのは,アクセスする前にAndroid本体で【アプリケーション】→【開発】を選択して出てくる,【USBデバッグ】にチェックを入れておくことです.これに気づかなくて,私は時間をえらく無駄にしてしまいました.ベンダIDは,/Developer/Applications/Utilities/USB Proberで探すことができます.

サンプルスクリプト

adbも動くようになったので,早速サンプルスクリプトを動かしてみました.すると,hello_world.plは動くのですが,test.plが動きません.最初に動かした際にはちゃんと動いたはずなのに,しばらくAndroidを使ってからまた試してみると,エラーが出て動かなくなりました.よく見ると,unicore/PVA.plがないとかエラーがでていました.これはAndroid.pmがJSONを使う際にutf8を考慮しない作りになっているからで,

$droid->getClipboard()

で取得するクリップボードの中身にマルチバイト文字が入ってると発生します.最初に使った際には,偶然マルチバイト文字が入っていなかったのでしょう.twitterで動かねーと騒いでいたら対応方法をHideaki Ohnoさんに教えてもらったので,ちょっとやりかた変えてAndroidに突っ込んでみました.Ohnoさんはdo_rpc()の中にでてくるto_json, from_jsonに{utf8 => 1}属性を入れていますが,encode_json, decode_jsonに変えれば動くんじゃないかと思って試してみたら動きました.そこで変更後のスクリプトは以下のようになります.
さらに,Android.pmを直接編集すると後でわからなくなると思ったので,pluginを作成しています.adbを使ってAndroid.pmがある/mnt/sdcard/com.googlecode.perlforandroid/extras/perl/site_perlディレクトリにAndroid/Pluginというディレクトリを作って,その中にMine.pmというモジュールを置きました.

package Android::Plugin::Mine;
use strict;

sub import {
    my $class = shift;
    *Andoid::do_rpc = sub {
        my $self = shift;
        if ( $self->trace ) {
            show_trace(qq[do_rpc: $self: @_]);
        }
        my $method  = pop;
        my $request = encode_json(
            {
                id     => $self->{id},
                method => $method,
                params => [@_]
            }
        );
        if ( defined $self->{conn} ) {
            print { $self->{conn} } $request, "\n";
            if ( $self->trace ) {
                show_trace(qq[client: sent: "$request"]);
            }
            $self->{id}++;
            my $response = readline( $self->{conn} );
            chomp $response;
            if ( $self->trace ) {
                show_trace(qq[client: rcvd: "$response"]);
            }
            if ( defined $response && length $response ) {
                my $result  = decode_json($response);
                my $success = 0;
                my $error;
                if ( defined $result ) {
                    if ( ref $result eq 'HASH' ) {
                        if ( defined $result->{error} ) {
                            $error = encode_json( { error => $result->{error} } );
                        }
                        else {
                            $success = 1;
                        }
                    }
                    else {
                        $error = "illegal JSON reply: $result";
                    }
                }
                unless ( $success || defined $error ) {
                    $error = "unknown JSON error";
                }
                if ( defined $error ) {
                    printf STDERR "$0: client: error: %s\n", $error;
                }
                if ( $Androd::Opt{trace} ) {
                    print STDERR Data::Dumper->Dump( [$result], [qw(result)] );
                }
                return $result;
            }
        }
        $self->close;
        return;
      }
}
1;

こうしておけば,test.pl の頭に追加モジュールをuseしておくだけで動作変更することができます.つまりこんな感じですね.

# Author: Sawyer X
# Email:  xsawyerx@cpan.org or xsawyerx@gmail.com

use strict;
use warnings;

use Android;
use Android::Plugin::Mine;
use Try::Tiny;

次は

test.plを動かすと

  • receiveEvent is deprecated

なんてメッセージがでてきます.次はこれを推奨のAPIに移植するのが目標です.