さまよえるプロジェクトチームまたはGoogle Developer Day 2011 Japanに行ってきた。

はい、いってきました。

で、今年こそ早々に記事を書こうと思っていたんだけど、このタイミングで会社の仕事が燃え上がってしまい、先週末は休みが取れなかった始末。だいたい、ろくすっぽ仕様も調整せずに納期だけは守ろうとか無理なんだよ。何のためにモックアップとか作ったんだ!なんちゃってアジャイルはいいかげんにしろ!これだから電 車 が ま い り ま す

...さて、話を戻そう。

もう2週間近くも前の話なのか。いつも通り青梅ライナーに乗って横浜へ向かった。
今年は閣下が何も言わなかったので、もとい会場がパシフィコ横浜だったのでいつもの通勤経路で行くことができた。




で、会場に着いてからまず受付の列に並び、次にTシャツの列に並び、その後、基調講演の列に...

ソビエト

そういえば、去年はTシャツは自宅まで郵送してくれたのだが、今年は「そこにTシャツあるから好きなの取ってってー」と言わんばかりに山積みになっていた。さらに「あんた朝ご飯食べてないでしょー!パンとコーヒーあるから食べていきなさーい!」と言わんばかりに朝食が用意されており、Googleの母親化が予想以上に進んでいた。

そんなGoogle赤軍の行列攻撃及びGoogleの母のおもてなし攻撃をかいくぐり、無事に[twitter:@saria]さん[twitter:@d0gf00d]さんと合流できた。ちなみに[twitter:@saria]さんとはTwitterで会話したことがある程度、[twitter:@d0gf00d]さんとはまったくの初対面だった。さらに言うと、合流したと言っても事前の打ち合わせは特になく、直前になって合流して一緒に回ろうと決めた次第。なんたる行き当たりばったり。

ところが、合流したのもつかの間、基調講演の列に並んでいる間に微妙にばらばらになってしまった。Google赤軍の攻勢が止まらない。




基調講演はやはりIce Cream SandwichとGoogle+の話が主だった。

で、基調講演の後に再び合流して、さて昼食と思ったのだが、Google赤軍がとどめに最大攻勢「昼食の列」を仕掛けてきたため、あえなく撤退。まずはDeveloper Sandboxを回ることにした。

ちょうど、基調講演の会場を出てすぐのところにブリリアントサービスさんのNFC QUESTの登録コーナーがあり、必要な人数(3人)も満たしていることから、参加してみることにした。このNFC QUESTというのは、会場の至る所にある端末にNFCのタグをかざしてレベルアップし、最後に魔王を倒すというものだった。
で、登録時に職業が決まるというので、見てみたら、

[twitter:@saria]さん: プログラマ
[twitter:@d0gf00d]さん: SE
[twitter:@headcubicle]: マネージャ

あの、クエ、クエ...ブリリアントサービスさん?
しかも私をマネージャにするとか微妙に過去のトラウマをえぐってくるし。
ちなみに魔王には挑んでみたものの、レベルが低くてデスマーチになって終わりだった。「負荷試験が残ってるぞ」とか言うのな。魔王。

ある程度Developer Sandboxを回って、多少は行列もましなっただろうと昼食を受け取りに行ったら、ちっとも行列が短くなってなかった。おのれ赤軍
しかし、ここで諦めると昼食抜きになってしまうので、赤軍をくいとめることにした。

さて、お楽しみの昼食である。去年はChrome丼とかあったので、今年は何かなーと思っていたのだが、普通のお弁当だった。少し残念。
さらに、昼食を食べる場所がなかなか見つからず、3人でしばらく会場をさまよっていた。




で、どうにか場所を見つけて、昼食を済ませた後にはBreakout Sessionの時間になっていた。

今年はHTML5関連のセッションを回ろうかなーと思っていたのだが、結局、興味の向くままに回ることにした。HTML5関連では特に目新しいことも無かったし、結果的に正解だったかもしれない。

ただ、HTML5関連で印象に残ったのは、「Google Chrome Frameとか使えばIE6でもHTML5のコンテンツが見られるよ!」としきりに言っていたことだ。本当に喜ばれるのか?これ。

で、適当にセッションを見て回ったり、Developer Sandboxに出展していた会社の上司のところへ行ったり、Chrome飴をもらったりしていたら、あっという間に18時になったので、Igniteを見にメインホールへ行った。GDD48のダンスがすごかったのだが、携帯のカメラしかなかったので写真を撮れなかった。すごく惜しい。

GDD終了後、3人で夕食を食べて、しばらく話をして、自宅に帰ったのが24時近く。いつも会社から帰ってくる時間と変わらなかった。さすがに翌日はへとへとだったものの、当初の目的は概ね達成したし、充実した1日だった。

ところで、あの3Dメガネは何だったんだろう。

とうとうMacBook Air(MC969J/A)を買ってしまった。

きれいな写真なんか撮れるはずがなかった。

話は2〜3週間前にさかのぼる。
会社でiPhoneアプリ検証用にMacBook Air(MC968J/A)を買ってもらったのが、そもそもの間違いだった。

確かに、元々MacBook Airが欲しいとは思ってはいたものの、まだMacBook Pro(MC371J/A)を買ってから1年ちょっとしか経っていなかったし、性能にも不満はなかった。そして何よりも家計が厳しく、さすがに今年Macを更新するのはためらわれた。

...ためらわれたはずだったのだが、前述した通り、会社でMacBook Airを買ってもらい、セットアップと称してさわってしまったのである。

軽くて薄い、動作もきびきびしている。

ちょうどその頃、通勤のお供にMacBook Proを持ち歩いていたのだが、やっぱり(物理的に)重いのが唯一の不満だった。

かくして、めでたくMacBook Air欲しい病が再発したのである。

とは言え、最初はまた買わずに終わるだろうと思っていた。なにしろ、どうせ買うならCore i7 + SSD 256GBと考えていて、どうあがいてもそんなお金はどこにも無かったのである。

ところが、Twitterに「MacBook Air欲しい病が再発」と書いたことにより事態は一変する。思えばTwitterに書き込んだことも間違いだったのだと思う。

Twitterに書き込んだら、MacBook Air欲しい病がうつったのである。[twitter:@mayupom]に。まさか空気感染するとは思わなかった。

で、これもまた良くなかった。[twitter:@mayupom]と話しているうちに「別にCore i5 + SSD 128GBでもいーや」と思えるようになってしまったのである。しかも、[twitter:@mayupom]によると1世代前のMacBook ProでもMacBook Airを買えるくらいの値段で売れるらしい。

買えてしまう。

あの(物理的に)重苦しい通勤から解放される。

頭を冷やす期間を1週間とっても、もはや無駄だった。口を開けば「MacBook Air欲しい。早く週末来ないかな」である。どうしてこうなった。

と言うわけで買ってきてしまった。メモリもSSDも容量が減って、CPUも少し遅くなったが満足している。何しろこの軽さと薄さである。バスでもオレンジのでもモノレールでも使えてしまう。ちなみに今日はオレンジのに乗っているときに家計簿をつけていた。MacBook Proにはできない芸当である。

金の話?金の話はよそうぜ。

androidで画面をロックする方法がよく分からない。

「帰省 持ち物」というキーワードでここにたどり着く人が後を絶たない。

さて、androidで画面をロックするコードを書いたものの、今ひとつよく分からない。
ソフトウェア技術ドキュメントを勝手に翻訳 - Android 開発ガイド l. デバイス管理を参考にして、なんとか必要そうなコードを拾ったものの、やっぱりよく分からない。

まずは、AndroidManifest.xmlだが、タグ以下を追加してデバイス管理を許可する。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="jp.headcubicle"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="10" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".LockTest"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="DarSample"
            android:permission="android.permission.BIND_DEVICE_ADMIN">
            <meta-data android:name="android.app.device_admin"
                android:resource="@xml/dar_sample" />
            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

次に、/res/xmlの下に"dar_sample.xml"という名前でファイルを作成し、デバイスポリシを定義する。
今回は画面ロックが出来ればいいので、のみ記述する。

<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
  <uses-policies>
    <force-lock/>
  </uses-policies>
</device-admin>

そして、一番よく分からないDeviceAdminReceiverのサブクラス。作成はしたものの、特に何をしているわけでもない。

package jp.headcubicle;

import android.app.admin.DeviceAdminReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * 何もしない。
 */
public class DarSample extends DeviceAdminReceiver {

    @Override
    public void onEnabled(Context context, Intent intent) {
    }

    @Override
    public void onDisabled(Context context, Intent intent) {
    }
}

メインのActivity。と言ってもボタンが1個あるだけ。あとはDevicePolicyManagerを取得したり、Intentでデバイス管理を有効にしたりしている。

package jp.headcubicle;

import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class LockTest extends Activity {
	
    static final int RESULT_ENABLE = 1;

    DevicePolicyManager mDevicePolicyManager;
    ComponentName mDarSample;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // DevicePolicyManagerを取得する。
        mDevicePolicyManager = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);
        // ComponentNameを取得する。
        mDarSample = new ComponentName(this, DarSample.class);
        // デバイス管理を有効にする。
        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
               mDarSample);
        startActivityForResult(intent, RESULT_ENABLE);

        setContentView(R.layout.main);
        
        Button lockButton = (Button) findViewById(R.id.lock_button);
        lockButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            	// 画面ロック
            	mDevicePolicyManager.lockNow();
            }
        });
    }
    
    /** ApiDemosそのまま */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case RESULT_ENABLE:
                if (resultCode == Activity.RESULT_OK) {
                    Log.i("DeviceAdminSample", "Administration enabled!");
                } else {
                    Log.i("DeviceAdminSample", "Administration enable FAILED!");
                }
                return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

最後にレイアウト用のmain.xml。ボタンを追加しただけ。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <Button android:id="@+id/lock_button"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="lock" />
</LinearLayout>

一応、動きはするが、やっぱりよく分からない。

androidでCanvasの上にButtonを置く

多少の罪悪感を感じつつエアコンを使っている。

さて、訳あってandroidCanvasの上にButtonを置かなくてはならなくなったので、少し調べたところ、以下のサイトが見つかった。

Create a Button on a Canvas - Android Beginners

何だか良く分からないが、RelativeLayoutの子としてCanvas(を持ったView)とButtonを定義してやれば、ButtonをCanvas上に好きなように置けるらしい。
ので、早速やってみた。

  • ButtonOnCanvas.java
package jp.headcubicle;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class ButtonOnCanvas extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(listener);
    }
    
    OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(ButtonOnCanvas.this, "Button on Canvas",
                    Toast.LENGTH_SHORT).show();
        }
    };
}


package jp.headcubicle;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;

public class CanvasTest extends View {

    public CanvasTest(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 背景色を設定
        this.setBackgroundColor(Color.WHITE);
        
        // 適当に図形を書く
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawCircle(200, 200, 60, paint);
        paint.setColor(Color.GREEN);
        canvas.drawRect(300, 300, 400, 400, paint);
        paint.setColor(Color.BLUE);
        Path path = new Path();
        path.moveTo(200, 300);
        path.lineTo(150, 400);
        path.lineTo(250, 400);
        canvas.drawPath(path, paint);
    }
}


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <jp.headcubicle.CanvasTest
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
    </jp.headcubicle.CanvasTest>
    <Button android:id="@+id/button"
        android:text="@string/button_text"
        android:layout_marginLeft="180px"
        android:layout_marginTop="250px"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</RelativeLayout>


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">ButtonOnCanvas</string>
    <string name="button_text">ButtonOnCanvas</string>
</resources>


  • 実行結果


もっとうまいやり方があるような気もするが、とりあえずこれでいいや。

ジンギスカンを食べに行ってきた。

[twitter:@mayupom]が忙しいらしく、今月の荻窪会は流会となった。
というわけで、[twitter:@shuniwa]と腹いせにジンギスカンを食べてきた。

本来であれば、ここからぐだぐだ説明するのだが、昨日、うっかりマインドマップ研修を受けてきたので、以降はマインドマップと写真でお楽しみください。






また行きたいなぁ。

AndroidでMicrosoft Translator V2 APIのSpeakメソッドを使う。

夏の計画停電。考えるだけでも恐ろしい。

さて、今回も例によって他人様のコードを借りまくりなのである。
そもそもAndroidMicrosoft Translator(Bing Translator)を使いたいなんて思う人が居るのかどうか疑問だが、私がそう思ってしまったんだからしょうがない。
Google Text-To-Speechで間に合えば良かったのだが、訳あって日中韓の3ヶ国語を喋らせなくてはならなかったので、Microsoft Translatorの出番となった。

まずは、Bing Translator ツール 翻訳 API - katamari.wankuma.comに書いてある通り、Bing Developer CenterでAppIDを取得する。

あとはMicrosoft Translator for Business - Microsoft Translator for Businessを見て、HTTPインターフェースでSpeakメソッドを呼ぶだけ。

としたいところなのだが、残念ながら返ってくる合成音声データはWAVE形式なので、AndroidのMediaPlayerでは再生できない。そこで、AudioTrackを使ってWAVEデータを再生する - Tech Boosterに書いてあるとおり、AudioTrackを使って再生する。

ちなみにHTTPリクエストについては、Make in HTTP request with android - stackoverflowを参考にした。

で、結果としてできあがったコピペコードがこちら。

    private void doTextToSpeech(String text) {
        String encodeText;
        String bingKey = [取得したAppId];
    	
        try {
            encodeText = URLEncoder.encode(text, "UTF-8");
            String strUrl = "http://api.microsofttranslator.com/v2/Http.svc/Speak?appId="
                        + bingKey + "&text=" + encodeText + "&language=zh-CHS";
            
            URI uri = new URI(strUrl);
            
            HttpClient httpclient = new DefaultHttpClient();
            HttpResponse response = httpclient.execute(new HttpGet(uri));
            StatusLine statusLine = response.getStatusLine();
            
            if(statusLine.getStatusCode() == HttpStatus.SC_OK){
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                response.getEntity().writeTo(out);
                out.close();
                byteData = out.toByteArray();

                // 必要となるバッファサイズを計算
                // なぜかAndroidエミュレータはサンプリングレート8kしか受け付けない。
                int bufSize = android.media.AudioTrack.getMinBufferSize(8000,
                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_8BIT);

                // AudioTrackインスタンス作成
                audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
                        8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_8BIT, bufSize,
                        AudioTrack.MODE_STREAM);
                
                // スレッドインスタンス作成
                Thread play_thread = new Thread(this);
                play_thread.start();
                
            } else{
                // コネクションを閉じる。
                response.getEntity().getContent().close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 再生用スレッド処理
    public void run() {
        if (audioTrack != null) {
            // 再生開始
            // ノイズが入らないように127バイト目から再生しているが、何バイト目から再生するのが
            // 正しいのか分からない。
            audioTrack.play();
            audioTrack.write(byteData, 126, byteData.length - 126);
        }
    } 

うん。参考にしたソースより明らかに劣化してる。

勢いで情報セキュリティスペシャリスト試験を受験することにした。

今更あけましておめでとうございます。

さて、情報セキュリティスペシャリストである。
28歳にもなって情報処理技術者試験とか悲しいにもほどがあるのだが、
勢いで申し込んじゃったんだから仕方がない。顛末は以下の通り。



「合格しねぇのかよ」とか「そんな問題出ねぇよ」とか「どこのテロリストだ」
とか色々と突っ込みはあるだろうが待って欲しい。重要なのは、

∠(゜д゜)/

が、「ウサイン・ボルト」だということだ。知らなかった。
というわけで、色々な意味で情弱であるにもかかわらず、友人([twitter:@mayupom])に
そそのかされて、情弱情報セキュリティスペシャリスト試験を受けることにして、
速攻で申し込み、友人にも早く申し込むよう促したのだが「週末申し込まなくては...」
などと悠長なことを言っている。そんなことが国際社会で許されるとでも思っている
のだろうか。

さて、受けるのは良いとして合格するかどうかだが、当然、全く自信はない。
とりあえず「ウサイン・ボルト」は覚えた。

∠(゜д゜)/