2011年12月30日金曜日

Chain Of ResponsibilityパターンによるFizzBuzz実装

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
出落ち感満載ですが、実装してみました。
単純にGoFデザインパターンの一つであるChain Of Responsibilityパターンを使ってみたかっただけです。
今まで実際に使った事が無かったので・・・。

Chain Of Responsibilityパターンって?


ggrks。
Wikipedia:Chain of Responsibility パターン
簡単に言うと責任の鎖。「ちょっと責任者に代わって。君じゃ埒があかん。」とこういう事ですね。


実装


ChainOfResponsibilityクラスのインナークラスとして全て書いています。
Fizz, Buzz, FizzBuzz, Integersクラスは抽象クラスであるHandlerを継承しています。
ChainOfResponsibility#fizzBuzz()の中でsetNextしてお互いを連結しています。

Handler#fizzBuzz(int)で実行。inCharge(int)で自分がこの値を担当しているかどうかをbooleanで返します。担当であればpring(int)を実行して終了。担当じゃなかったら自分の次の人のHandler#fizzBuzz(int)を呼び出します。

例えば65という値が入れられた場合はFizzBuzz->Fizz->Buzzまで行ってprintされます。

なんとなくFizzBuzzクラスやIntegersクラスのinCharge(int)はFizzクラスとBuzzクラスを使って判定させてます。
連結の仕方によって計算量が変わる気がしますがなんだか面倒なので考えるのをやめました。
もっと短く出来ると思いますが面倒なので(ry

public class ChainOfResponsibility {
    static public abstract class Handler{
        private Handler next = null;
       
        public Handler setNext(Handler next){
            this.next = next;
            return next;
        }
        public final void fizzBuzz(int value){
            if(inCharge(value)){
                print(value);
            }else if(next != null){
                next.fizzBuzz(value);
            }else{
                throw new RuntimeException("Oops!");
            }
        }
        protected abstract boolean inCharge(int value);
        protected abstract void print(int value);
    }
    
    static class Integers extends Handler{
        Fizz fizz = new Fizz();
        Buzz buzz = new Buzz();
        @Override
        protected boolean inCharge(int value) {
            return !fizz.inCharge(value) && !buzz.inCharge(value);
        }
        @Override
        protected void print(int value) {
            System.out.println(value);
        }
    }
    static class Fizz extends Handler{
        @Override
        protected boolean inCharge(int value) {
            return value%3 == 0;
        }
        @Override
        protected void print(int value) {
            System.out.println("Fizz");
        }
    }
    static class Buzz extends Handler{
        @Override
        protected boolean inCharge(int value) {
            return value%5 == 0;
        }
        @Override
        protected void print(int value) {
            System.out.println("Buzz");
        }
    }
    
    static class FizzBuzz extends Handler{
        Fizz fizz = new Fizz();
        Buzz buzz = new Buzz();
        @Override
        protected boolean inCharge(int value) {
            return fizz.inCharge(value) && buzz.inCharge(value); 
        }
        @Override
        protected void print(int value) {
            System.out.println("FizzBuzz");
        }
    }
    
    public void fizzBuzz(){
        Handler integer = new Integers();
        Handler fizz = new Fizz();
        Handler buzz = new Buzz();
        Handler fizzBuzz = new FizzBuzz();
        
        fizzBuzz.setNext(fizz).setNext(buzz).setNext(integer);
        for(int i = 1; i <= 100; i++){
            fizzBuzz.fizzBuzz(i);
        }
    }
    
    public static void main(String[] args) {
        new ChainOfResponsibility().fizzBuzz();
    }
}

まとめ


このパターンはいろいろ使えそうだなぁ。
デザインパターンによるFizzBuzz実装をシリーズ化できるかなー。どうかな。

2011年12月26日月曜日

Androidアプリケーションのパーミッション改竄を検知するスニペット

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

はじめに



AppNetBlockerというINTERNETパーミッションを消しちゃうアプリが出ました。
そもそもアプリ改竄自体いいのかどうか。まぁ怪しいパーミッション消したいとかいうのも判るけども。
AppNetBlockerに限らず実際にはINTERNETパーミッションどころじゃなくこんなアプリ↓まで既にAndroid Marketでは販売されています。

https://market.android.com/details?id=com.gmail.exathink.appshield

一部でapk改竄は著作権の侵害に当たるといった声もありますねー。しらんけど。
僕はよくわからないんでとりあえずなんとも言えないですけど、

パーミッション改竄されたかどうかは検知しとくかー

という感じです。


実装


どっかのUtilクラスにでもコピペして下さい。

    private static List<String> missingPermissions(Context context, String[] expectations) {
        return missingPermissions(context, new ArrayList<String>(Arrays.asList(expectations)));
    }
    private static List<String> missingPermissions(Context context, List<String> expectations){
        if (context == null || expectations == null) {
            return null;
        }
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS);
            String[] info = pi.requestedPermissions;
            if (info != null) {
                for (String i : info) {
                    expectations.remove(i);
                }
            }
            return expectations;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

使い方


missingPermissionsにアプリケーションが本来持っているべきパーミッションのリストを渡すと、消えてる奴が返ってきます。
改竄されてなければ空リストが返るはずです。nullの時は多分何かエラーが発生していると思います。知らん。

import android.Manifest.permission;
//〜〜略〜〜
List<String> missing = missingPermissions(this, 
    new String[]{permission.INTERNET, permission.WRITE_EXTERNAL_STORAGE}
);
if(mission != null && !missing.isEmpty()){
    //なんか改竄されてる!
}

こういう風に使えるかもしれないですね



おわりに


Twitterを引き続き注視していきまーす。


2011年12月18日日曜日

え、まさか難読化の為にわざわざproguard.cfgをいじってるんですか?

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

photo by http://www.flickr.com/photos/jonnygoldstein/

Android Advent Calendar 2011 12/18(日)担当の@sys1yagiです。

いきなりですがproguard.cfgをいじるのってクソめんどくせーわけですよ。何が悲しくてあんな意味不明なオプションの文法を毎回ググりながらsitと言いながら書かないといけないのか。ていうか複数人でやってたらproguard.cfgをいじるタイミングがさー色々面倒でもう最後にぱっと追加するかーとか皆思っちゃってて最後にカオスになったりして\(^o^)/とかさ、もっと簡単な方法はねーのかよ!?例えばアノテーションでさー、クラスのとこに書いたらさー、難読化されないとかさ。

あるでー


はい、ProguardがそもそもAnnotation用のライブラリを提供しています。これを使えば難読化の対象外にしたいクラスに所定のアノテーションを書くだけで難読化から逃れられます。普通にProguardが提供している機能なので、全くトリッキーな事なしでいけます。ちょっと副作用がありますが


導入の準備


ProguardはオープンソースでSoruceForgeで公開されています。
アノテーションによる難読化の回避はこのSourceForgeで配布されているアーカイブに含まれているライブラリを利用します。ダウンロードページからproguard4.6.zipをダウンロードして下さい(多分4.6じゃなくてもいけると思いますが)。
http://sourceforge.net/projects/proguard/files/



導入


proguard4.6.zipを展開し、proguard4.6/examples/annotations/lib/に入っているannotations.jarannotations.proをAndroidプロジェクトにコピーして下さい。annotations.jarはビルドバスを通しておきます。今回はproject/libsに置きました。


するとアノテーションで@Keepとか書けるようになります。


次にプロジェクト直下あたりのproguard.cfgの先頭辺りに以下の二行を記述します
-dontshrink
-include libs/annotations.pro
annotations.proはproguard.cfgと拡張子は異なりますが中身は大体同じです難読化についてのオプションが記述されています。-dontshrinkは書かないと何故かアノテーションが消えるので書きます。すかさず-keepattributes **を思いついたあなたはやっぱり凄い!ですがこのオプション書いてもダメでした。四の五の言わずに書いて下さいませね。

これだけでおk


実行


あとはProguardにかけるだけ。そういやAntで直接Proguardやれば-dontshrinkはいらないかもしれません。ここではEclipseのAndroid Toolsで"Export Signed Application Package ..."とかやっています。
難読化実施後のクラス構成


見事@KeepをつけたDonotObfuscationクラスが難読化されていません。
メソッドはどうでしょう。javapしてみます。


@KeepNameのついたnotObfuscation()は難読化されていませんね。obfuscation()の方は難読化されている事がわかります。
Keep以外にも色々なアノテーションがあります。色々使えそうですね。


副作用


良い事ばかりじゃもちろんありません。ちょっと副作用がありますんで注意して下さい。


不要クラスが残る


-dontshrinkは利用していないクラスなどの削除を行なってバイナリサイズを小さくしてくれる処理を禁止します。つまりProguardにかけた対象となるクラスが難読化後全て残ります。物によってはapkサイズがでかくなってしまいます。ただ、ほぼ気にしなくてもいいレベルだと思います。精々数KBの違いでしょう。これなら他のリソースの容量削った方が効率的だと思います。


何を難読化したくないかが一覧できない


proguard.cfgにKeepオプションなどを書く場合は全部書かれているので一覧性がありますが、アノテーションの場合はソースに個別に書くのでどのクラスが難読化したくないかについて分かりにくくなります。まーKeepとかでソース検索すりゃいいしどーでもいい気もしますがー


そういえば


annotations.proの中身を見てみるとこんな感じです。

-keep @proguard.annotation.Keep class *

-keepclassmembers class * {
    @proguard.annotation.Keep *;
}

つまりふつーにProguardのKeepの条件に「指定のアノテーションがある場合難読化しねーぜ」、と設定してるだけです、つまり別にProguardの提供しているannotations.jarを使わなくても自分で適当にアノテーションを定義して、annotation.proみたいなのを書いてincludeするかProguard.cfgに書くだけでいけるわけです。つまりそういう事です。


結論


皆楽になるといいですねー。
メリークリスマスー
明日は@patorash@Nkznさんです。わーい。

2011年12月16日金曜日

Honeycomb以降ではMotionEventのACTION_DOWNでevent.get(1)すると死ぬ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

まさかこんな実装をしているわけがない、と思います。僕くらいです。はい。おおonTouchメソッドよ死んでしまうとは情けない。

こうすると死ぬ


常識的に考えて以下のコードは気持ち悪いですよね。でもAndroid3.0未満、つまりGingerbread以前では問題なく動作します。しかしHoneycomb以降ではjava.lang.IllegalArgumentExceptionが発生してしまいます。エラーメッセージはpointerIndex out of rangeです。なんででしょうね?!

@Override
public boolean onTouchEvent(MotionEvent event) {
 switch(event.getAction() & MotionEvent.ACTION_MASK){
 case MotionEvent.ACTION_DOWN:
  int x = event.getX(1);
  break;
 }
}


ソースを読む


MotionEventのソースを見ると・・・。げぇ全然違うぜぇえええ。

Android2.3.3_r1
public final float getX(
 int return mDataSamples[mLastDataSampleIndex
  + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
}

Android4.0.1
public final float getX(int pointerIndex) {
        return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
    }


どう変わったか


上記の4.0.1のソースではもはやnativeメソッド。さすがに実装まで追うのはしんどいんで省略しますが、2.3.3_r1では配列アクセスをしている事がわかります。この配列はMotionEventのコンストラクタで初期化されます。配列サイズはコンストラクタに渡したpointerCount, sampleCountと定数NUM_SAMPLE_DATAを乗じた数となります。

private MotionEvent(int pointerCount, int sampleCount) {
 mPointerIdentifiers = new int[pointerCount];
 mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
 mEventTimeNanoSamples = new long[sampleCount];
}

現タッチ数は内部でmNumPointersで管理しています。2.3.3_r1のgetX(int)では利用していません。つまりgetX(int)でアクセスするインデックスに影響を与えません。MotionEventはイベントを受け取るViewで使い回しているので(一応MotionEventのhasCode()で確認した)、コンストラクタで確保された配列をオーバーしない限りは死なない事になります。

操作 実タッチ数 Gingerbread以前 Honeycomb以降
event.getX(0) 2
event.getX(2) 2 ×
event.getX(100000) 2 × ×


結論


今までギリギリ動いていたけどHoneycomb、ICSで爆死、そんな事があるかもしれませんね。怖いわー。




2011年11月14日月曜日

Activity#runOnUiThread(Runnable)の実装を読む&注意点

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
実はrunOnUiThreadを最近まで知らなくていつもアホみたいにHandler#postしていてなんだか悔しかったのでActivity#runOnUiThread(Runnable)の実装を読んでみました。

Activity#runOnUiThread


Activity#runOnUiThreadの実装がこちら。

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

内部的にHandler#postしていますね。
ここでちょっと気になるのが
Thread.currentThread() != mUiThread
この比較です。runOnUiThreadを実行しているスレッドがUIスレッドかどうかチェックしています。
そしてUIスレッドである場合は
action.run();
指定したRunnableをrunOnUiThreadの中で即実行しています。つまりrunOnUiThreadはRunnableを実行する為にブロッキングされるという事です。

どんな時問題になるか


runOnUiThreadをHandler#postと同じモノだと考えて利用しているとハマる可能性があります。
まぁ多分大丈夫と思いますが以下のhello2()様な実行順に依存性があるような実装をしちゃうとヤバイですね。

Handler mHandler = new Handler();
String mMessage = "hello!";
private void hello(){
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(getApplicationContext(), mMessage, Toast.LENGTH_SHORT).show();
        }
    });
    mMessage = "good bye!";
}
private void hello2(){
    runOnUiThread(new Runnable(){
        @Override
        public void run() {
            Toast.makeText(getApplicationContext(), mMessage, Toast.LENGTH_SHORT).show();
        }
    });
    mMessage = "good bye!";
}

別スレッドからhello(),hello2()、UIスレッドからhello(),hello2()を実行するとToastに何が表示されるでしょうか?

呼び出しパターン 結果
別スレッドからHandler#postを呼ぶ(hello()) "good bye!"が表示される
UIスレッドからHandler#postを呼ぶ(hello()) "good bye!"が表示される
別スレッドからrunOnUiThreadを呼ぶ(hello2()) "good bye!"が表示される
UIスレッドからrunOnUiThreadを呼ぶ(hello2()) "hello!"が表示される

なんか一個動きが違うのがいるーーーーー。
はいそういう事ですね。


結論


色々省略しますが
・Handler#post, runOnUiThreadに渡すRunnableが触る変数にはHandler#post, runOnUiThread呼び出し後触らない。
・Handler#post, runOnUiThreadはなるべくメソッドの最後に呼ぶ
・Handler#post, runOnUiThreadに渡すRunnableの中では呼び出し元のメンバ変数などを触らない。
Handler#post, runOnUiThreadを呼び出すメソッド内でfinalな変数を宣言しておいてそれにアクセスしたりする様にする。

とかやってるといいんじゃないでしょかー


追記



といったリプライが。確かに。(@atsushienoさんありがとうございました!)

実際hello(), hello2()のmMessage = "good bye!";の直前にThread.sleep(1000);を追加して実行してみるとどうなるか実験してみました。
結果は以下です

呼び出しパターン 結果
別スレッドからHandler#postを呼ぶ(hello()) "hello!"が表示される
UIスレッドからHandler#postを呼ぶ(hello()) "hello!"が表示される
別スレッドからrunOnUiThreadを呼ぶ(hello2()) "good bye!"が表示される
UIスレッドからrunOnUiThreadを呼ぶ(hello2()) "hello!"が表示される

なんか一個動きが違うのがいるーーーーー。
はいそういう事ですね。
以下の様な事になります。

呼び出しパターン 動き
別スレッドからHandler#postを呼ぶ(hello()) 呼び出し後の処理とRunnableの実行どちらが先かは判らない
UIスレッドからHandler#postを呼ぶ(hello()) 必ず呼び出し後の処理が実行された後にRunnableが実行される
別スレッドからrunOnUiThreadを呼ぶ(hello2()) 呼び出し後の処理とRunnableの実行どちらが先かは判らない
UIスレッドからrunOnUiThreadを呼ぶ(hello2()) 必ずRunnableが実行された後に呼び出し後の処理が実行される


結論は変わらず。
どちらにせよHandler#post, runOnUiThreadを呼び出した後にHandler#post, runOnUiThread内部で利用されうる値などの書き換えはやばいよ、という事ですね。
多分誰もこういう事はしないとは思いますが・・・。多分。


2011年11月9日水曜日

JavaによるErrorSortの実装

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
また画期的なソートアルゴリズムが出ましたね。
[Python]SleepSort、BogoSortに続く画期的なソートアルゴリズム、ErrorSortを考えた
で、こういうネタソート、何か作れないかなーと思って考えていたら、「あっ、そうだ。エラーを使ってソートすればいいじゃないか」と思って実装してみた。

面白いので早速Javaで実装してみました。


ErrorSort


ソートはintでやる事になるのでTをintに変換するインタフェースを設けました。
try-cacthの部分で ToインタフェースのIntメソッドを呼び出してTのint表現をもらって配列を作り、
インクリメントされていくindexで配列にアクセスしてIndexOutOfBoundsExceptionを起こさせています。

 public interface To<T> {
  public int Int(T t);
 }
 public static <T> List<T> errorSort(List<T> list, To<T> to){
  List<T> copy = new ArrayList<T>(list);
  List<T> result = new ArrayList<T>();
  int size = copy.size();
  int count = 0;
  int index = 0;
  WHILE:while(count < size){
   for(T t : copy){
    try{
     (new byte[to.Int(t)])[index] = 0;
     }catch(IndexOutOfBoundsException e){
     result.add(t);
     copy.remove(t);
     count++;
     continue WHILE;
    }
   }
   index++;
  }
  return result;
 }


実行


以下の様に使います。

 public static void main(String[] args){
  List<Integer> list = Arrays.asList(new Integer[]{2,4,60,3,23,44});
  list = errorSort(list, new To<Integer>(){
   @Override
   public int Int(Integer t) {
    return t;
   }
  });
 }

結果
2,3,4,23,44,60,


結論


まだまだいろんなソートアルゴリズムがありそうですね。

2011年11月7日月曜日

CoffeeScriptでライフゲーム作った

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
CoffeeScriptおもしれー
という事でライフゲームを作成しました。
HTML側はもう書かずエレメントの生成も全部CoffeeScriptでやってみました。

ライフゲーム


lifegame - jsdo.it - share JavaScript, HTML5 and CSS


コード


ライフゲームのルールはWikipediaで→ライフゲーム - Wikipedia

CoffeeScriptの実装はこの辺を適当に参照しつつ。

#util methods
create = (type) -> document.createElement(type)
append = (child) -> document.body.appendChild(child)
br = -> append(create("br"))
text= (type, text) ->
 elm = create(type)
 elm.innerHTML = text
 append(elm)
input = (type, value) ->
 elm = create("input")
 elm.type=type
 elm.value = value
 elm
button= (value, fn) ->
 elm = input("button", value)
 elm.onclick = fn
 append(elm)
 
#consts
CELL_SIZE = 5
BOARD_SIZE = 350
LENGTH = BOARD_SIZE/CELL_SIZE

isStart = false

cells = new Array(Math.pow(LENGTH, 2))
for i in [0.. cells.length-1]
 cells[i] = {"now":0, "next":0}

#methods
draw = (canvas, context) ->
 context.fillStyle="#eeeeee"
 context.fillRect(0,0, canvas.width, canvas.height)
 context.fill()
 
 context.fillStyle="#000000"
 context.strokeStyle="#000000"
 context.lineWidth = 1
 x = canvas.width/CELL_SIZE
 y = canvas.height/CELL_SIZE
 for i in [0.. x]
  context.beginPath()
  context.moveTo(i*CELL_SIZE , 0)
  context.lineTo(i*CELL_SIZE , canvas.height)
  context.stroke()
 for j in [0.. y]
  context.beginPath()
  context.moveTo(0, j*CELL_SIZE)
  context.lineTo(canvas.width, j*CELL_SIZE)
  context.stroke()   
 for c,i in cells
  if c.now == 1
   x = Math.floor(i % LENGTH)
   y = Math.floor(i / LENGTH)
   context.fillRect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE)

initialize = ->
 canvas = document.createElement("canvas")
 canvas.width = BOARD_SIZE
 canvas.height = BOARD_SIZE
 context = canvas.getContext("2d")
 canvas.onmousedown = (e) -> 
  x = Math.floor(e.offsetX / CELL_SIZE)
  y = Math.floor(e.offsetY / CELL_SIZE)
  if cells[x+(y*LENGTH)].now == 0
   cells[x+(y*LENGTH)].now = 1
  else
   cells[x+(y*LENGTH)].now = 0
  draw(canvas, context)
 append(canvas)
 draw(canvas, context)
 br()
 text("span","speed")
 speed = input("text", "10")
 speed.size = 10
 append(speed)
 br()
 button("start", ->
  if !isStart 
   isStart = true
   start(speed.value)
 )
 button("stop", ->
  isStart = false  
 )
 button("one step", ->
  if !isStart
   start(speed.value)
 )
 button("reset", ->
  for i in [0.. cells.length-1]
   cells[i] = {"now":0, "next":0}
  draw(canvas, context)
 )
 button("random", ->
  for i in [0.. cells.length-1]
   if Math.floor(Math.random() * 10) + 1 > 3
    cells[i].now = 0
   else
    cells[i].now = 1
  draw(canvas, context)
 )
 start = (speed) ->
  calc()
  draw(canvas, context)
  if isStart
   setTimeout(-> 
    start(speed)
   , 1000/speed)
 calc = ->
  for c, i in cells
   count = add(i-1)
   count+=add(i+1)
   count+=add(i-1-LENGTH)
   count+=add(i+1-LENGTH)
   count+=add(i-LENGTH)
   count+=add(i-1+LENGTH)
   count+=add(i+1+LENGTH)
   count+=add(i+LENGTH)
   switch count
    when 0,1,4,5,6,7,8
     c.next = 0
    when 3
     c.next = 1
    when 2
     c.next = c.now
  for c in cells
   c.now = c.next
 add = (index)->
  if index >= 0 && index < cells.length
   cells[index].now
  else
   0
window.onload= ->
 initialize()



結論


CoffeeScriptいい!

2011年11月5日土曜日

Chromium(ブラウザ)をソースからビルドする for Mac

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
2011年11月1日(火)にパシフィコ横浜で開催されたGoogle Developer Dayに行って来ました。
で、デベロッパーツールのティップス・アンド・トリックスというセッションでChromeのデベロッパーツールのtipsなどを聞いて「あーChrome面白いなー」と思ったので早速コード落としてビルドしようと思って既に数日過ぎました。
最近Mac Book Airを買ったのでMacでビルドします。


Chromiumについて


Chromiumはhttp://dev.chromium.org/でオープンソースプロジェクトとして開発されています。
ブラウザであるChromiumとOSであるChromium OSは別々になっています。
Macでのビルド方法はhttp://code.google.com/p/chromium/wiki/MacBuildInstructionsに書かれていますが英語だしすぐ忘れるので備忘録として書いていきます。


環境構築


Chromiumをビルドする為に必要な環境を構築しましょう。

必要な環境


MacでChomiumのソースを取得し、ビルドする為に必要な環境は以下の通りです。

  • Intel Mac 10.6 (“Snow Leopard”)以上
  • XCode 3.2.3以上
  • gclient
  • git client(なくてもよいっぽい)


  • XCodeのインストール


    言わずと知れたIDEです。最初から入っている気もしますが入ってなかったりバージョンが足りてない場合はダウンロードしてインストールする必要があります。
    Apple Developer Connectionのアカウントが必要です。
    http://developer.apple.com/xcode/でゴニョゴニョして入手して下さい。
    LionならApp Storeから入手も可能。無料で手に入ります。


    gclientのインストール


    depot_toolsと呼ばれるパッケージに含まれるgclientというモノ。
    以下のsvnコマンドで取得します。
    svn co http://src.chromium.org/svn/trunk/tools/depot_tools
    

    depot_toolsをPATHに通しておきましょう。
    vi ~/.bash_profile
    
    export PATH=$PATH:〜〜/depot_tools
    


    gitのインストール


    多分最初から入ってる気がするけどこの辺りを参照してインストールしてください。


    コードの取得


    gclientを使ってChromiumのソースを取得してみます。
    gclientの他にtarballで配布されていたりgit cloneしたりも出来ます。

    まずソースを落としてくる為のディレクトリを作成します。名前はなんでもいいです
    mkdir chromium
    

    作ったディレクトリの中に移動し、以下のコマンドを実行します。
    cd chromium
    gclient config http://src.chromium.org/svn/trunk/src
    

    chromiumディレクトリ内に.gclientというファイルが生成されます。
    以下のコマンドを実行する事でソースをダウンロードする事ができます。
    gclient sync
    

    srcというディレクトリが生成され、その中にソースコードがダウンロードされます。


    ビルド


    ソースコードのダウンロードが完了したらいよいよビルドです。

    ビルドの準備


    LionやXCode4系の場合いろいろ問題があるっぽいのでここではClangを使ってビルドをします。
    srcディレクトリに移動し、以下のコマンドを実行し準備をします。
    cd src
    
    GYP_GENERATORS=make GYP_DEFINES=clang=1 ./build/gyp_chromium
    

    Lionを利用している場合は以下のコマンドを実行して下さい。
    GYP_GENERATORS=make GYP_DEFINES=mac_sdk=10.6 ./build/gyp_chromium
    


    デバッグビルド


    ビルドの準備ができたらmakeでビルドします。
    make chrome -j4
    

    chromium/src/out/DebugにChromium.appが作成されます。


    リリースビルド


    リリースビルドは以下のコマンド
    BUILDTYPE=Release make chrome -j4
    

    chromium/src/out/ReleaseにChromium.appが作成されます。


    実行


    わーい。
    デバッグビルドだとかなり動作が重いです。
    リリースビルドだと大体現在リリースされているChromeと同じ様な速度が出ます。



    2011年9月24日土曜日

    Google Apps ScriptでGoogle Sitesのページタイトルを再起的に編集する。

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    Google Apps Scriptっていったいどの程度のことができるんだろーなーとググってもだいたいGoogle Docsのスプレッドシートをナニするとかどーでもいい事しか出てこないからまぁ手を出してなかったんですが、「Google Sitesのページを操作したい、しかしページが多い、どうしたらいいのかー」とかいった問題に直面し、Google Apps Scriptでなんとかならんのかーと思ってこの辺りのDefault Servicesにずらーっとサービスが並んでいてSites ServicesとかいういかしたAPIがあったのでやってみたら出来たのでメモ。

    Google SitesでGoogle Apps Scriptを使う


    Google Sitesにログインして「サイトを管理」のページに行きます。



    「サイトを管理」の①「Apps スクリプト」を開き、②「新しいスクリプトを追加」を押下してスクリプトを追加します。



    するとこんな感じの画面が現れます。ここでGoogle Apps Scriptを追加、編集できます。



    Google Sitesのページのタイトルを編集する


    Google Sitesのページの編集は以下の手順で行います。

  • SitesAppクラスのgetSite(name)を使ってSiteクラスを取り出す
  • SiteクラスのgetChildren()を使ってSiteのトップレベルのページ一覧(Page[])を取得する
  • PageクラスのgetChildren()を使ってサブページを再起的に処理する


  • でソースが以下
    function myFunction() {
      var site = SitesApp.getSite("サイト名");
      var pages = site.getChildren();
      for(var i = 0; i < pages.length; i++){
        exec(pages[i], "-");
      }
      
      //実行ログをメールで送信
      MailApp.sendEmail("メールアドレス", "タイトル", Logger.getLog());
    }
    function exec(page, padding){
      var pages = page.getChildren();
      for(var i = 0; i < pages.length; i++){
        exec(pages[i], padding+"-");
      }
      var old = page.getTitle();
      var title = page.getTitle();
      var num = parseInt(title[0]);
      if(isFinite(num)){
        title = (num+1) + title.substring(1, title.length);
        page.setTitle(title);
      }
      Logger.log(padding + old + "->" + page.getTitle());
    }
    

    exec(page, padding)で再起しつつ、後半の処理でページタイトルを修正しています。
    今回は"タイトルの一文字目が数値ならインクリメントする"処理をしています。
    まーなにも役に立ちません。が全ページ一括で何かできます。
    SiteApp以外にもMailAppを使ってメールを送っています。編集処理の結果をメールで送信してます。
    ちなみに"サイト名"というのはサイトを作る時に設定したサイトの識別子のことです。
    http://sites.google.com/site/サイト名/
    これですね。

    実行


    トリガーとかで定期実行したり、ページに貼付けたり出来るらしいです、がこういうちょっとしたスクリプトなら一回実行するだけでいいやーという場合が多いのでもうこの編集画面で実行しちゃいます。


    実行ボタンで実行。
    すると色々承認しろよな、とか出てきます。個人なら承認しちゃえばいいんですが、例えば会社で使ってたりするとちょっと大変かもしれませんね。




    まとめ


    結構色々できるなー。
    たまにGoogle Apps Scriptのドキュメント眺めるといいかも。

    2011年9月19日月曜日

    【java】closeする用メソッド

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    多分便利なライブラリがあるんだろうなーと思いつつ、ファイル操作はいつもFileInputStream,InputStreamReader,BufferedReaderなどを使ったりしていて、finallyのcloseとかめんどくさい感じになっていて、以下のメソッドをいつも使っているけど意外と便利だったのでメモ。

    コード


    どんなオブジェクトをぶち込んでもとにかくcloseしてくれる君。
    void close()なメソッド持って無くてもスタックトレースが吐かれまくるだけ。

    public static void close(Object hasCloseMethod) {
    	if (hasCloseMethod == null) {
    		return;
    	}
    	try {
    		hasCloseMethod.getClass().getMethod("close", (Class< ? >[]) null).invoke(hasCloseMethod, (Object[]) null);
    	} catch (Exception e) {
    		e.printStackTrace();
    	}
    }
    


    まとめ


    なんかいいライブラリ教えて下さい・・・。

    2011年9月12日月曜日

    GDD2011jp DevQuiz @sys1yagi編 149.0/150.0

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク


    全部やろうと思ったけどチャレンジクイズェ・・・にやられ手が出せなかった。


    分野別クイズ Web Game



    問題


    神経衰弱のゲーム問題です。ウェブページにカードが表示され、カードをクリックすると裏返って色が表示される。
    全ての色のペアを開けばクリア。全部で64問。



    回答


    最初は8枚とかなので手動でクリアできますが、後半1024枚の問題などがあって厳しいです。
    Googleは「そういう時はChrome Extensionsを使えよな」と言っていたので使いました。
    特定ドメインのページが開かれたらonload辺りで記述した処理を実行してくれるエクステンションがサンプルでついていたのでそれを改造しますた。
    まずカードの要素配列を作成して、0番目とi+1番目をめくって、色が同じなら要素配列から削除し、要素配列がなくなったら完了、という直球な実装。Chrome Extensionsでやったので全自動で64問解いてくれました。bookmarklet的にも使えるはず。
    めくった履歴とか保存してないので要素数によっては時間がすごくかかる、しかしマシンスペックのお陰で問題なし。
    ありがとう現代。

    (function(){
    	var $ = function(id){return document.getElementById(id); }
    	var es = [];
    	for(var i = 0 ;; i++){
    		var e = $("card"+i);
    		if(e == null){ break; }
    		es[i] = e;
    	}
    	var e = document.createEvent('MouseEvents');
    	e.initEvent('click', false, true);
    	var i = 0;
    	while(es.length > 0){
    		es[0].dispatchEvent(e);
    		es[i+1].dispatchEvent(e);
    		if(es[0].style.backgroundColor == es[i+1].style.backgroundColor){
    			es.splice(0,1);	es.splice(i,1);
    			i = 0;
    		}
    		else{
    			i++;
    		}
    	}
    })();
    

    感想


    Extensionsってonload的なのできるんだぁへぇ。




    分野別クイズ Android



    問題


    AIDLとAndroidアプリを配布するんでそのアプリを端末なりエミュで立ち上げて指定のアカウントアドレスとパスコード入れ、配布AIDLからAndroidアプリのサービスに繋いでString getCode()で取り出せた値を答えろや。

    回答


    簡単。aidlとかサービスとかAndroidやってたら一瞬。

    	IQuizService service;
    	ServiceConnection conn = new ServiceConnection(){
    		@Override
    		public void onServiceConnected(ComponentName name, IBinder binder) {
    			service = IQuizService.Stub.asInterface(binder);
    			try{
    				((TextView)findViewById(R.id.result)).setText(service.getCode());
    			}catch(Exception e){
    				e.printStackTrace();
    			}
    		}
    		@Override
    		public void onServiceDisconnected(ComponentName name) {
    			service = null;
    		}
    	};
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            Intent intent = new Intent(IQuizService.class.getName());
            if ( this.bindService(intent, conn, Activity.BIND_AUTO_CREATE) ) {
            	
            }
        }
    

    感想


    簡単すぎワロス。Androidエンジニアならapkを解析して値を取り出すべきだったのだけど結局やらなかった。
    dex2jarとかでいけたかなぁ。




    チャレンジクイズ



    問題


    テキストにスライドパズルの定義が書かれていて、一行につき一問、以下のフォーマットで5000問。一問0.01点。3x3〜6x6まで。
    幅,高さ,パズルの配列
    例えば以下のようなやつ
    5,5,806E91245K=3=AJ=7HIF=CMNO
    "="は壁で移動できない。"0"は空白。5x5なので
    806E9
    1245K
    =3=AJ
    =7HUF
    =CMNO
    というパズルになる。それを操作して解法を出す。
    操作はL,R,U,D(左右上下)。"0"を動かした手順を記録する。
    上記問題だとゴールは
    12345
    6789A
    =C=EF
    =HIJK
    =MNO0
    になる。

    回答


    長くなるのでダイジェストで。8/30の夜から着手。問題に取り掛かった時は探索アルゴリズムなんて全く知らなかったです。
    他の方々と違って?泥臭く、運の要素も強かった感があります。

    1.
    なにこれ余裕じゃね。サクっとランダムで回答導いて長さを最適化するべしだな

    2.
    フレームはできた、一発実行だな。→死亡

    3.
    あ、あかん、これはあかん。オライリーの「アルゴリズムクイックリファレンス」を引っ張り出す。
    探索木とかいう所を読むが全然意味わかんねー。

    4.
    俺は、俺のやり方でやるんだ。

    5.
    謎ロジック完成。3x3は大体正解へ。(今にして思えば幅優先もどきのロジック)

    6.
    へへ、あとは全部回すだけ、勝ったな。

    7.
    4x4が、倒せない、だと・・・? 199/5000

    8.
    アルゴリズムクイックリファレンス」にごめんなさいして再入門。反復深化深さ優先探索がいいというので盲目的版をやってみる。ちょっと解けて来た。 471/5000

    9.
    下限値枝刈り法とかいうオシャレな方法があったので追加してみる。このへんで1000台には乗った。
    下限値なんとかは単純に壁を考慮しないMDにした。 1011/5000

    10.
    色々深さとか調整しまくったりロジックを最適化してぶん回し1300くらいまでいった。 1378/5000

    11.
    まぁボーダー越えたやろ、という事で幅優先の時代に戻ってみよう、と実装を一からやってみた。
    幅優先探索の場合深くなるとキューが爆発するので、キューサイズの限界値を設定し、ノードをMDのスコアでソートして、スコア上位ノードだけ次のノードを生成するという方法にしてみた。試しに最大キュー10000で深さ150まで、でやってみたら3000問解けた。 3542/5000

    12.
    この最大キューと深さを調整しまくる。実行時引数に取り、バッチにして様々な値で計算。4000台突入。 4002/5000

    13.
    処理最適化を行う。特にMDのスコア計算が毎回配列全ぶん回しだったのでそれをやめる。一回の操作でスコアは+1,-1しかしないのでその辺をいじったら早くなった。また移動後の盤面を保存し、以前の盤面に到達してしまったノードは無効にする様にしたら4900へ。

    14.
    伸び悩んで来たので最後のあがき。フィーリングカップルアルゴリズムという適当に考えた奴をやってみた。
    最初の状態→ゴール、ゴール→最初の状態の2つの探索を走らせて、それぞれの盤面履歴を保存していく。相手の盤面履歴を参照して自分の現在の盤面と一致したら相手の履歴と自分の履歴を結合してFA。→メモリ食うし残った問題には歯が立たず。これってもしかして双方向のなんとかってやつなんだろうか。
    次にMDをもうちょい賢くしてみる。壁が存在する場合最短手数が変わるのでそこを考慮した。でもあんまり意味なかった。

    最終的には4902問。98問残り。となった。

    感想


    舐めてかかっていたが相当強かった。TwitterのTL上の他の方々の解法がレベル高すぎてついていけない。
    でも色々調べてやってみる事は大事だなと。探索木なんて全く知らなかったが今回の問題によってIDDFSやBFS,A*とかMDとかいろいろ知った。完全に理解が出来ていないモノもあるし、まだまだ色々な方法があったりするけどとにかく面白くてワロタ。
    4900に行くまでにユーティリティ的な部分以外は5回くらい1から作りなおした。
    他にもHTML5でCanvas使って問題を読み込んで手作業で解くプログラムや、GAEにロジックを乗せてタスクキューで解かせたり色々した。
    諦めず殴り続ける事が大事だと思った。

    汚いソースはこちら->github
    この提出版は結局いじり過ぎで結構重くなってます。ビルドして直下のlooper.batかlooper.shを叩けば4900は出るはず。多分。
    まじ酷いソース。まじ参考になりません。

    ソース整理はmasterブランチでやる予定。多分。



    2011年7月20日水曜日

    折角なのでABC2011 Summerで発表した資料、破棄した資料をうp

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    折角なんでうpだ。

    SlideShare意外と簡単に使えるのね。

    発表内容はAndroid3.0のグラフィクス、マルチコア、メディア関連です。

    ABC2011 Summerで発表に使った方。


    ABC2011 Summerの直前だか何かに破棄したやつ。

    2011年7月18日月曜日

    ABC2011 Summerで発表に参加するなど

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク


    ABC2011 Summerに参加してきましたー。
    開発トラック1の13:00からAndroid3.0/3.1/3.2で追加されたAPIについてデ部の一人として話してきました。
    @youten_redoさん
    @korodroidさん
    @bols_blueさん
    @95kugoさん
    @vvakameさん
    @cyberspacefarm(miyaken)さん
    @yanzmさん
    @chun_ryoさん
    お疲れ様でした!


    発表した資料はこちら
    http://bit.ly/pnvSCl ※PDF 171ページ 30MB
    自分は78P~101Pまで。


    とりあえずKPT法らしきもので色々振り返り。

    キープ


  • 発表する

  • 今まだ発表とかやった事なかったので、たまたま運良く機会に巡りあえ、
    自分の未熟さとかもろもろもろもろ色々勉強になりまくった。
    川があったら飛び込め、の精神でやったのが良かったと思う。
    どんどん空気読まず参加に手を上げていく。
    でも妻1子3ある身なので月1,2回くらいに留める!


  • 外に出る

  • 出不精+育児という事で最近全然開発者系の催し物に行ってないし、ましてや発表なんかした事もないと来ていて、
    これはいかんなーと思っていたわけで、やっぱ今回やって良かったなぁーと。
    特にTL上、ML上では見聞きした事ある方々に実際会えたってのは大きいなぁと。
    あんなスーパーな方々と肩を並べて発表出来るなんて!という。
    調子に乗ってもっとやる!


    プロブレム


  • 詰め込みすぎた

  • 最初は発表資料を「こういうAPIとかが追加されて、こうやって使う事ができるよー」というノリで書いていったのだけど、自分担当の所だけで50Pくらいになってしまって、リハしたら持ち時間の4分を当然大幅にオーバー。
    前日に全部作り直した。
    外向けの発表資料は今回が初めてでどういう風に書いたらいいかとかもわからず、
    全力でやったら割と格好良さげなのが出来たけど結局全部破棄。
    勉強になったのでこれも良い経験だけども4分という持ち時間とテーマから「こういう粒度、深度でこのくらいの量だよね」っていう所を目測出来るようにがんばらないといけないなと。


  • フォーマットがバラバラ過ぎた

  • これは全体的な問題だけど、やっぱフォーマット統一した方が良かっただろうなぁと。
    フォーマット統一出来たなら統一した方が良かったなぁーーと。
    チェックとか大変だけど、見に来られた方的にどうだったんだろう・・・。という。


  • もっとお手伝いできる事があったはず

  • 今回は担当分の整理が終わった後は自分担当の部分ばかりに集中していて、
    「どういう見せ方がいい」とかあれやこれやの議論に参加していなかった。
    結局youtenさんやvvakameさんmiyakenさんなどなどに任せっきりに。
    もっと色々お手伝いできたよなぁと。お手伝いします。これからは。


    トライ


  • 一人で発表する

  • 発表やってみると、「あーこれ発表ネタになるな」とか「LTでいけそうだな」とかアイデアというか
    そういうのを考える様になりました。こりゃ調子に乗ってもっとやらないとな、と。
    超怖いけど@tomorrowkey先生もやってたし、やるしかねーだろ、と。
    やれる機会あるのか知らんけど。ありそうだったら手を挙げる。多分。


  • モノを出す

  • 以前にドラムピッカーを作って公開したりもしましたが全然メンテせず。放置。
    これはいけない。次はちゃんとやる。多分。
    次はCSVに書いたらContentProviderとDAO吐くやつを出すはず。多分。Gitも勉強しないと。


  • 貢献する

  • もっと色々やれるはずだ、という事。
    DDMSについてはあるタイミングでADTのMLなどに相談するかもういきなりAOSPに修正差分出すとか。やる。多分。



    おまけ


    ちょっとだけ回りました。

    8号館4Fのバザー。物理的にテストする感じのやつ

    某社のSPモードのなんかいい感じの位置情報に基づいていい感じの情報を収集するやつ

    アンドロイダーバッヂをゲット。ついでにポチのカード。
    ポチはBIGLOBEさんがやっている合法的な投げ銭システム。だったと思う。

    2011年6月27日月曜日

    【Android】ListViewのデフォルトの区切り(divider)を普通のレイアウト上で使っちゃいたい

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    ListViewのデフォルトの区切り。
    自前でShaderだのDrawableなxmlを作ってもいいんですが、デフォルトがあるんだからそれ使ったらいいじゃないのっていう。

    準備


    適当に以下の様なレイアウトファイルを作成する。
    仮にdivider.xmlとする
    <View xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent" android:layout_height="1dp"
     android:background="?android:attr/listDivider" />
    

    使う


    レイアウト上でincludeして使います。

    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical" android:layout_width="fill_parent"
     android:layout_height="fill_parent">
     <include layout="@layout/divider" />
     <Button android:text="Button" android:id="@+id/button1"
      android:layout_width="wrap_content" android:layout_height="wrap_content">
     <include layout="@layout/divider" />
     <Button android:text="Button" android:id="@+id/button2"
      android:layout_width="wrap_content" android:layout_height="wrap_content">
     <include layout="@layout/divider" />
    </LinearLayout>
    


    ほうほう。

    これはAndroid1.6の場合です。Android2.1以降ではListViewのdividerもシンプルになってちょっとあれだなぁと思ったり。


    おわり


    dividerに困ったらもうこれ使っとけ、みたいな。

    2011年6月22日水曜日

    【Android】DDMSのタグフィルターを正規表現で絞っちゃいなよ(配布もあるよ)

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    タグの絞り込みで正規表現使えたらいいかもなーくらいに思っていたのでやりました。
    ちょっと長いけど導入自体は超簡単です。うんちくが長いだけです。

    ※利用は自己責任でヨロシクお願いします☆

    タグで正規表現


    こんな感じ。suffixが"Service"であるタグを絞り込みする例

    配布物


    ddms_add.jarってのを使います。ていうか作りました。
    https://sites.google.com/site/goatprog/android/androidproject/ddms_add.jar?attredirects=0&d=1

    試す人はとりあえず落として下さい。


    どーやったか


    DDMSはJavaの住人です。もうなんでもありです。日本語対応の時も書いた気がするな・・・。
    という事で、以下の手順でいい感じにクラスを上書きしています。

    1. エントリポイントになるクラスを作ってDDMSのMainの代わりに実行
    2. DDMSのMainを呼ぶ前に書き換えたいクラスをClass.forName()で読み込む。
    3. DDMSのMainを呼び出す。

    こんだけです。クラスパスがポイントですね。書き換えたいクラスを含むパスを前に持って来るだけでおk。
    Androidのmasterブランチから持ってきたDDMSのソースをいじってビルドして出来たクラスを使っています。


    改造内容


    いじったのはたったの一行です。
    $ANDROID_HOME/sdk/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogFilter.java
    boolean accept(LogMessage logMessage) {
            // do the regular filtering now
            if ((mMode & MODE_PID) == MODE_PID && mPid != logMessage.data.pid) {
                return false;
            }
    
            if ((mMode & MODE_TAG) == MODE_TAG && (
                    logMessage.data.tag == null ||
                    //logMessage.data.tag.equals(mTag) == false)) {
                 logMessage.data.tag.matches(mTag) == false)) { //ここ
                return false;
            }
          -----略-----
        }
    

    このクラスはlogcatをフィルタリングする際の各種条件を保持しているクラスです。
    logcatから新たなログが送られてくると、各タブは各自が持つLogFilterのaccept(LogMessage)メソッドを呼び出して、
    自分の領域にログを表示するかどうか判定をします。
    タグの部分は元々equals(Object)で判定されていました。それをmatches(String)に変えただけです。
    本当はPattern#compile(regex)して出来たPatternを保持した方が毎回String#matches(String)を実行するよりいいのですが、修正範囲が広がるのでやめました。動作上全く影響ないです。


    課題


    実は今まで知らなかったのですが、フィルターは":"と"|"が使えません。
    どうもEclipseのプラグイン上のDDMSや、DDMS単体のタブ情報を保存する為に、":","|"をセパレータとして使っているみたいです。
    なので今回の修正では完全な正規表現は使えません。

    LogFilterにはLogFilterの中身を書きだすtoString()とStringからLogFilterを復元するloadFromString(String)があります。
    ここを改造してあげる必要があるんですねーーーー。

    @Override
        public String toString() {
            StringBuilder sb = new StringBuilder(mName);
    
            sb.append(':');
            sb.append(mMode);
            if ((mMode & MODE_PID) == MODE_PID) {
                sb.append(':');
                sb.append(mPid);
            }
    
            if ((mMode & MODE_LEVEL) == MODE_LEVEL) {
                sb.append(':');
                sb.append(mLogLevel);
            }
    
            if ((mMode & MODE_TAG) == MODE_TAG) {
                sb.append(':');
                sb.append(mTag);
            }
    
            return sb.toString();
        }
    
        public boolean loadFromString(String string) {
            String[] segments = string.split(":"); //$NON-NLS-1$
            int index = 0;
    
            // get the name
            mName = segments[index++];
    
            // get the mode
            mMode = Integer.parseInt(segments[index++]);
    
            if ((mMode & MODE_PID) == MODE_PID) {
                mPid = Integer.parseInt(segments[index++]);
            }
    
            if ((mMode & MODE_LEVEL) == MODE_LEVEL) {
                mLogLevel = Integer.parseInt(segments[index++]);
            }
    
            if ((mMode & MODE_TAG) == MODE_TAG) {
                mTag = segments[index++];
            }
    
            return true;
        }
    
    こんな感じで":"使ってますね。各値をBase64か何かでエンコード/デコードすりゃいいんじゃね、と思います。
    今回はやりません。


    導入手順 Windows編


    簡単です。
    ddms_add.jarを以下の場所に配置してください。つーかddms.jarがある所と同じ場所。
    $ANDROID_SDK_HOME/tools/lib/
    そしてddms.batをちょっといじります。
    とりあえずddms.batをコピーしてddms_add.batとかにでもして下さい。
    やる事は
    • クラスパスを追加
    • メイン呼び出しを書き換え
    です。
    ddms.batの一番下でjavaを実行しています。これをちょっと書き換えます。

    call %java_exe% %java_debug% -Dcom.android.ddms.bindir=%prog_dir% -classpath "%jarpath%;%swt_path%\swt.jar" com.android.ddms.Main %*
              ↓
    call %java_exe% %java_debug% -Dcom.android.ddms.bindir=%prog_dir% -classpath "./lib/ddms_add.jar;%jarpath%;%swt_path%\swt.jar" jp.dip.sys1.android.ddms.Main %*

    こんだけ。あとは"ddms_add"をコマンドプロンプトで叩くとか。


    導入手順 Mac編


    Windows編とほぼ同じです。略します。
    ddms.batの代わりにddms.shか何かがいるはずです。それを書き換えます。

    #exec "$javaCmd" -Xmx256M $os_opts $java_debug -Dcom.android.ddms.bindir="$progdir" -classpath "$jarpath:$swtpath/swt.jar" com.android.ddms.Main "$@"
              ↓
    exec "$javaCmd" -Xmx256M $os_opts $java_debug -Dcom.android.ddms.bindir="$progdir" -classpath "lib/ddms_add.jar:$jarpath:$swtpath/swt.jar" jp.dip.sys1.android.ddms.Main "$@"

    最後に


    【緩募】
    もうちょい色々検討した上でpatchを投げたいとか思っているわけですが、
    その辺全くしらないので手とりナニとり教えてくれる有識者の方教えて下さい・・・。


    追記2011/6/22 14:18
    今すぐフォローすべきAndroid界のスーパーエンジニア@keiji_ariyamaさんからadt-devのメーリングリストで聞け!というアドバイスを頂きました!やってみたいと思います。
    ありがとうございました真・今すぐフォローすべきAndroid界のスーパーエンジニア@keiji_ariyamaさん!

    2011年6月21日火曜日

    Java脳でもわかるObjective-C入門

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

    photo: Some rights reserved by yukiv


    Javaと比較しながら「へぇーObjective-Cってそういう感じか」と理解した気になろう。

    流れ




    軽くジャブ


    まずは以下のソースを眺める

    //SampleClass.h
    #import <Foundation/Foundation.h>
    #import "SampleClassDelegate.h"
    
    @interface SampleClass : NSObject <sampleclassdelegate>{
     int index;
     NSString *str;
    }
    @property(assign) int index;
    @property(nonatomic, retain) NSString *str;
    
    +(void) dump : (SampleClass*) instance;
    -(void) setData : (NSString*) str  str: (NSString*) number;
    -(void) setData : (NSString*) str  num: (NSInteger) number;
    -(NSString*) toString;
    
    @end
    
    //SampleClass.m
    #import "SampleClass.h"
    @implementation SampleClass
    @synthesize index;
    @synthesize str;
    +(void) dump:(SampleClass *)instance{
     NSLog(@"%@", [instance toString]);
    }
    -(void) setData : (NSString*) text  str: (NSString*) number{
     self.str = text;
     self.index = [number intValue];
    }
    -(void) setData : (NSString*) text  num: (NSInteger) number{
     self.str = text;
     self.index = number;
    }
    -(NSString*) toString{
     return [NSString stringWithFormat:@"%@:%d", self.str, self.index];
    }
    //delegate
    -(NSString*) toStringDao{
     return [NSString stringWithFormat:@"%@:%d%@", self.str, self.index, @"だお"];
    }
    @end
    
    さすがのSyntaxHighlighterさんもObjective-Cはサポートしてねーって事でC++でハイライト。
    ハイライトしなさ杉。
    うんざりする見た目だけど、これから説明する事を読めばスラスラ理解できるようになりますよ!



    クラスファイルの構成


    Javaなら"クラス名.java"なわけですが、Objective-CはC言語の上に乗ってる言語なので、
    その辺はCの仕組みに準拠します。
    C/C++同様にヘッダファイルと実装ファイルに分かれてます。分けなくてもいいけど分けた方がいいので分けてます。
    XCodeでクラスを新規作成した場合は以下の2ファイルが作成されます。
    "クラス名.h" //ヘッダ
    "クラス名.m" //実装ファイル

    まーCです。



    コメント

    全く何もかも同じ

    ■Java

    //コメント
    /* コメント */
    

    ■Objective-C

    //コメント
    /* コメント */
    


    import文


    Objective-CはC言語と違って#includeではなく#importを使うようになっています。
    違いは二重includeを自動的に防いでくれるだけです。#ifndefとかそういう制御がいらんという事です。
    Javaしか知らない人はこの辺は気にしなくていいです。#importって書いておけばいいんです。

    ■Java
    import java.lang.string;
    

    クラスパスが通っていれば読み込めます。


    ■Objective-C
    #import <UIKit/UIKit.h>
    #import "Moge.h"
    

    上の書き方は標準ライブラリから探す感じ(ほんとは違います)
    下の書き方はプロジェクトの中から探す感じっぽい雰囲気。多分



    変数の定義


    概ねJavaと同じです。



    ■Java
    int i = 10;
    

    ■Objective-C
    int i = 10;
    


    参照


    クラスオブジェクトは必ず参照型です。Javaと同じですね。
    書き方は"クラス名*"です。

    ■Java
    Object object;
    

    ■Objective-C
    NSObject *object;
    


    id型


    javaでいう処のObject型。
    どんなオブジェクトでも入れられる型です。生ポインタという認識で大体いいかもしれません。
    クリックイベントなどの引数でsenderをidで渡すといった形で使われます。

    ■Java
    String object1 = new String("あはは");
    Object object2 = object1;
    

    ■Objective-C
    NSObject *object1 = [[NSObject alloc] init];
    id object2 = object1;
    

    ちなみにidにプリミティブ型を突っ込むと、警告が出ます。
    Javaだとコンパイルエラーになりますね。



    クラス定義


    Javaと違って、Objective-Cではまずクラスを宣言します。その後実装コードを書きます。
    一般的には宣言はヘッダに、実装は実装ファイルに書いていきます。
    また、宣言と実装で宣言子が異なります。

    宣言


    ■Java
    宣言のみとかってねーよ。(ないよね?)


    ■Objective-C
    @interface Moge : NSObject{
    //メンバ変数宣言
    }
    //メソッド宣言
    @end
    
    キモイんですが、 @interface クラス名 : スーパークラス名で始まり
    { }の中にメンバ変数を宣言し、その後メソッドを宣言、最後に @end で閉じます。
    メンバやメソッドは宣言のみで初期化や実装をここで書くことは出来ません。
    この宣言を書いたヘッダファイル(.h)を実装ファイル(.m)でimportして実装を行います。


    実装


    ■Java
    public class Moge{
    
    }
    

    ■Objective-C
    #import "Moge.h"
    @implementation Moge
    //メソッドの実装などなど
    @end
    
    ヘッダをimportし、そこに宣言されたクラスを実装します。
    宣言子は@implementationです。
    ヘッダで宣言したメソッドの実装をガリガリ書きます。(実際の実装例はもうちょい下でやります)



    メソッドの宣言、実装


    メソッドには二種類あります。クラスメソッドとインスタンスメソッドです。
    Javaでいう所のstaticメソッドとメンバメソッドですね。一緒ですね。
    ヘッダのクラス宣言の所で各メソッドを宣言していきます。
    ちなみにヘッダで宣言したメソッドは全てpublicになります。
    privateなメソッドはmファイル内に実装だけいきなり書くことで実現する事が出来ます。


    ■宣言の仕方!


    まぁキモイんですが慣れです。
    -(void) moge : (NSString*) str isSave : (BOOL) isSave;
    
    そうです、":"で色々区切ってます。":"の後に引数を書いていくイメージですね。
    最初見たとき「えっ」ってなりました。意味不明ですよねこれ。
    つまりこういう事です

    -(戻り値の型) メソッド名 : (引数の型) 引数名 ラベル名 : (引数の型) 引数名

    さて気になるのが「ラベル名」です。これ実はいりません。なくてもビルド通ります。
    どういう時に使うかっていうとオーバーロード的な事をしたいときに使うんですね。
    Javaなら勝手にやってくれるってーのにねー。やれやれですね。

    -(void) moge : (NSString*) str isSave : (BOOL) isSave;
    -(void) moge : (NSString*) str num : (NSInteger) number;
    
    呼び出し側でラベル名を指定しながら引数をぶっこむ事でメソッドの呼び分けができるわけですね。
    オーバーロードの際はラベル名付けて無いとコンパイル時に怒られます。

    さて勘のいい方は気がついたかもしれません。
    メソッド宣言の先頭に"-"とかいって毛が生えていました。
    これ気のせいじゃないです。"-"には意味があります。
    先頭の記号はメソッドの種類を現しています。



    クラスメソッド


    Javaで言うところのstaticなメソッド。
    クラスをインスタンス化しなくても使えます。

    ■Java

    public static void moge(String str, Boolean isSave){}
    


    ■Objective-C

    +(void) moge : (NSString*) str isSave : (BOOL) isSave;
    
    先頭を"+"にするとクラスインスタンスである、という事になります。


    ■インスタンスメソッド


    いわゆる普通のメソッド。

    ■Java

    public void moge(String str, Boolean isSave){}
    


    ■Objective-C

    -(void) moge : (NSString*) str isSave : (BOOL) isSave;
    



    メッセージ式


    基本的に見た目がキモいだけです。色々と高尚な何かがあるみたいですけど、気にしなくてもいいです。

    ■Java

    Integer num = new Integer(120);
    num.toString();
    


    ■Objective-C

    NSNumber* num = [NSNumber numberWithInt:120];
    [num stringValue];
    

    「メッセージ式」でググると色々出てくるので興味ある方はアレして下さい。
    引数付きのコンストラクタも特殊です。ていうかコンストラクタは無いです。
    allocの後に初期化メソッドを呼びます。引数付きのコンストラクタは引数付きの初期化メソッドで実現するんですねー。
    上の例ではallocすらしてませんね、ファクトリメソッド的な物っぽいですね。多分そうです。



    プロパティ


    Javaでいうgetter/setterを隠蔽してくれる感じの仕組みです。
    いろんな設定ができますがココでは省きます。

    ■Java

    public void setIndex(int index){
      this.index = index;
    }
    public int getIndex(){
      return this.index;
    }
    

    ■Objective-C

    プロパティも宣言と実装的なものが分かれます。

    ・宣言
    これはクラス宣言の{}の後に書きます。
    @property(オプション的な何か) 名前;

    こういう感じです。
    @interface Moge : NSObject{
     NSInteger index;
    }
    @property(assign, nonatomic) NSInteger index;
    @end
    


    ・実装的な感じ
    @synthesize index;
    

    上記の例だと自動的にgetter/setterが見えない所で実装されています。
    色々と他にも使い方がありますがとりあえずこれだけでおkな気がします。
    これによって以下の様な書き方ができます。

    NSInteger a = self.index;
    self.index = 10;
    
    内部的にはgetter/setterメソッドが実行されています。

    詳細はプロパティの宣言と実装とか読んで下さい。



    セレクタ


    いわゆるコールバックです。関数ポインタみたいなもんです。
    Javaで言うとjava.lang.reflect.Methodだなぁというのが個人的なイメージ。

    ■Java

    public class Moge{
      //コールバック
      public void callback(String str){
        System.out.println(str);
      }
      public void caller(Object target, Method callback)throws Exception{
        callback.invoke(target, "あはは");
      }
      public static void main(String[] args){
        try{
          Moge moge = new Moge();
          //リフレクションで取り出す
          moge.caller(moge,Moge.class.getMethod("callback", String.class));
        }catch(Exception e){
          e.printStackTrace();
        }
      }
    }
    

    ■Objective-C

    Objective-Cでは@selectorでメソッドを取りだす事ができます。
    取り出したメソッドはSEL型という型になります。
    コールバックを実現するメソッドは引数にセレクタを受け取って結果をセレクタに返してやります。
    メディア再生のフレームワークとか使う時にセレクタをセットしてメディアの再生ステータスの変更を受け取ったり、
    ボタンにセットしてクリックの通知を受ける時などに使います。

    @implementation Moge
    //コールバック
    -(void) callback:(NSString*) str{
      NSLog(@"%@", str);
    }
    -(void) caller:(id) target callback:(SEL) callback{
      NSInvocation* invoker = [NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:callback]];
      NSString* string = @"あはは";
      [invoker setTarget:target];
      [invoker setSelector:callback];
      [invoker setArgument:&string atIndex:2];
      [invoker invoke];
    }
    
    -(void) exec{
      //@selectorでcallback:(NSString*) strを渡す
      [self caller:self callback:@selector(callback:)];
    }
    @end
    



    プロトコル


    iOSではデリゲートと呼ばれるものです。Javaでいう所のインタフェースにかなり近いです。
    ていうか一緒です。

    ■Java
    //宣言
    public interface Cat{
      public String getBreed();
    }
    //実装
    public class Mike implements Cat{
      @Override
      public String getBreed(){
        return "tortoiseshell cat";
      }
    }
    


    ■Objective-C
    //プロトコルの宣言
    @protocol Cat
    -(NSString*) getBreed;
    @end
    
    //クラスの宣言
    @interface Mike : NSObject < Cat >
    //書かなくてもいけるよ
    @end
    
    //クラスの実装
    @implementation Mike
    -(NSString*) getBreed{
      return @"tortoiseshell cat";
    }
    @end
    

    クラス宣言の最後に< プロトコル名達 > でプロトコルの実装を宣言できます。
    プロトコルは複数実装できます。Javaのインタフェースと同じっすね。
    プロトコルはiOSではデリゲートとして頻繁に登場します。
    例えばWebView(UIWebView)。
    AndroidではWebViewClientを継承してshouldOverrideUrlLoadingを実装しますが、
    iOSではUIWebViewDelegateというプロトコルを実装してセットしてあげる感じになります。
    その他アプリケーションのライフサイクルの通知もデリゲートです。



    カテゴリ


    カテゴリはちょっと異質で、javascriptのプロトタイプ拡張みたいな雰囲気を持っています。
    Javaには無い機能です。超便利です。

    ■Java
    m9(^Д^)

    ■Objective-C
    書き方はクラスと大体同じです。

    @interface 拡張するクラス (カテゴリ名)

    例えば全てのクラスの基底であるNSObjectに適当にメソッドを追加するなら
    @interface NSObject (Unko)
    -(void) buriburi;
    @end
    
    こんな感じでヘッダに書き、普通に実装します。
    ファイル名は判りやすいように"NSObject+Unko.h"という風に作られる事が多いです。それしか見た事ないす。
    拡張したNSObjectが使いたい人は"NSObject+Unko.h"をimportするだけです。
    それだけでいつでもburiburiメソッドを呼べます。凄いっすね。
    Objective-Cじゃなきゃできません。

    もしObjective-Cじゃなかったら、そんなのObjective-Cじゃないんです



    文字列リテラル


    Objective-Cでの文字列リテラルでは、先頭に@を付ける必要があります。
    @"文字列"はNSStringのリテラル表現となります。

    ■Java
    String str = "moge";
    Object str2 = "hoge";
    

    ■Objective-C

    NSString* str = @"moge";
    id str2 = @"hoge";
    



    ログ


    ログは重要すなぁ。Javaでいう標準出力なんですが、ほんとに標準出力なのか怪しいのでログという事にしました。

    ■Java
    System.out.println("わーい");
    


    ■Objective-C
    NSLog(@"わーい");
    



    ひと通り使ってみる感じ


    では最後にこれらの機能をひと通り使うクラスなどを載せてみます。

    SampleClassDelegate.h
    //プロトコル(デリゲート)
    #import <UIKit/UIKit.h>
    
    @protocol SampleClassDelegate
    -(NSString*) toStringDao;
    @end
    

    SampleClass.h
    #import <Foundation/Foundation.h>
    #import "SampleClassDelegate.h"
    
    @interface SampleClass : NSObject <SampleClassDelegate>{
     int index;
     NSString *str;
    }
    @property(assign) int index;
    @property(nonatomic, retain) NSString *str;
    
    +(void) dump : (SampleClass*) instance;
    -(void) setData : (NSString*) str  str: (NSString*) number;
    -(void) setData : (NSString*) str  num: (NSInteger) number;
    -(NSString*) toString;
    
    @end
    

    SampleClass.m
    #import "SampleClass.h"
    @implementation SampleClass
    @synthesize index;
    @synthesize str;
    
    +(void) dump:(SampleClass *)instance{
     NSLog(@"%@", [instance toString]);
    }
    
    -(void) setData : (NSString*) text  str: (NSString*) number{
     self.str = text;
     self.index = [number intValue];
    }
    
    -(void) setData : (NSString*) text  num: (NSInteger) number{
     self.str = text;
     self.index = number;
    }
    
    -(NSString*) toString{
     return [NSString stringWithFormat:@"%@:%d", self.str, self.index];
    }
    //delegate
    -(NSString*) toStringDao{
     return [NSString stringWithFormat:@"%@:%d%@", self.str, self.index, @"だお"];
    }
    
    @end
    

    SampleClass+Suffix.h
    //カテゴリ
    #import "SampleClass.h"
    @interface SampleClass (Suffix)
    -(void) toStringSuffix:(id) target callback:(SEL) callback;
    @end
    

    SampleClass+Suffix.m
    #import "SampleClass+Suffix.h"
    @implementation SampleClass (Suffix)
    -(void) toStringSuffix:(id) target callback:(SEL) callback{
     NSInvocation* invoker = [NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:callback]];
     NSString* string = [NSString stringWithFormat:@"%@%@", self.str, @"っていう"];
     [invoker setTarget:target];
     [invoker setSelector:callback];
     [invoker setArgument:&string atIndex:2];
     [invoker invoke];
    }
    @end
    

    User.h
    //SampleClassを利用するクラス
    #import <Foundation/Foundation.h>
    #import "SampleClass.h"
    #import "SampleClass+Suffix.h"
    @interface User : NSObject {
    }
    -(void) exec;
    @end
    

    User.m
    #import "User.h"
    @implementation User
    
    -(void) exec{
     SampleClass* sample = [[SampleClass alloc] init];
     id<SampleClassDelegate> delegate = sample;
     
     NSNumber* num = [NSNumber numberWithInt:120];
     [num stringValue];
     
     
     [sample setData:@"一回目" num:100];
     NSLog(@"%@", [sample toString]);
    
     [sample setData:@"二回目" str:@"50"];
     NSLog(@"object:%@", [sample toStringDao]);
     NSLog(@"delegate:%@", [delegate toStringDao]); 
     //セレクタ
     [sample toStringSuffix:self callback:@selector(callback:)];
     
    }
    -(void) callback :(NSString*) str{
     NSLog(@"callback:%@", str);
     [str release];
    }
    @end
    
    

    実行結果:
    一回目:100
    object:二回目:50だお
    delegate:二回目:50だお
    callback:二回目っていう


    まとめ

    ね、簡単でしょ?

    2011年6月14日火曜日

    【Android】 ACTION_EXTERNAL_APPLICATIONS_AVAILABLEはこねーよ!

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    photo: Original Update by Origamiancy

    昨夜未明、私はSDカードへのインストールをサポートしたアプリに新規機能を追加していました。
    その機能はAlarmManagerを使って色々登録するので、端末の再起動時にACTION_BOOT_COMPLETEDによってそれらをリストアしてやらんといけませんでした。
    技術的には簡単だったので颯爽と実装を終えた私は意気揚々とテストに取り掛かりました。
    さてリストアはうまくいったかな?

    が・・・だめっ・・・!

    何度端末を再起動しても、マニフェストファイルを見直してもダメ。
    全く同じ設定の他アプリは受け取っているのにこの新機能を追加したアプリだけは受け取れません。

    30分ほどハマってようやく気が付きました。

    SDカードにインストールしてるアプリはACTION_BOOT_COMPLETEDを受け取れないんじゃね?

    全くそのとおりでした。
    長い夜の始まりでした。



    SDカードにインストールされていた場合ACTION_BOOT_COMPLETEDは受け取れない


    そうです、SDカードにアプリがインストールされていた場合ACTION_BOOT_COMPLETED(android.intent.action.BOOT_COMPLETED)は受け取れないんです。
    確かに、SDカードのマウントより前に飛んでるイメージがありますね。
    実際ドキュメントにも書かれています。

    App Install Locationの"Applications That Should NOT Install on External Storage"に以下の記載があります。
    Broadcast Receivers listening for "boot completed"
    The system delivers the ACTION_BOOT_COMPLETED broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
    訳:SDカードにインストールしてるアプリにACTION_BOOT_COMPLETEDはこねーよ! neverだよnever!

    ではどーしたらええの?



    ACTION_EXTERNAL_APPLICATIONS_AVAILABLEというアクションが追加されている


    同ドキュメントにはまた、こんな記載もあります。
    Your running Service will be killed and will not be restarted when external storage is remounted. You can, however, register for the ACTION_EXTERNAL_APPLICATIONS_AVAILABLE broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
    訳:Serviceもほんとはダメだけど、と、特別にACTION_EXTERNAL_APPLICATIONS_AVAILABLEを登録してたら再起動してあげるんだからね!

    つまりACTION_EXTERNAL_APPLICATIONS_AVAILABLEを受け取るサービスを定義しておいてナニすればいいってことー!?



    いやACTION_EXTERNAL_APPLICATIONS_AVAILABLEはSDカードにインストールされているアプリ側は受け取れないぽい


    試す前に色々調べていると、以下のissueが。

    Issue 8485: Documentation bug: ACTION_EXTERNAL_APPLICATIONS_AVAILABLE

    ACTION_EXTERNAL_APPLICATIONS_AVAILABLEってSDカード内のアプリは受け取れなくねー?との事。
    確かにリファレンスのACTION_EXTERNAL_APPLICATIONS_AVAILABLEの方では以下の記載が

    ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
    Note that the packages in this list do not receive this broadcast. The specified set of packages are now available on the system.
    訳:extraに乗ってくる利用可能リストに含まれるパッケージはこのbroadcastを受け取れないよー!(つまりSDカードがマウントされて有効化されるアプリ自身はこのbroadcastは受け取れない!)



    ドキュメントバグじゃね


    Issue 8485にこんなコメントが
    This is a real bug. The apps installed on external storage will not receive ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.
    訳:ACTION_EXTERNAL_APPLICATIONS_AVAILABLEはこねーよ!



    明日があるさ


    もしかしたらServiceで動的にACTION_EXTERNAL_APPLICATIONS_AVAILABLEを受け取るBroadcastReceiverをregisterしておくとシステムが空気読んで再起動してくれるのかもしれません。でもなんだか試すのが面倒なので諦めました。


    全てを諦めた私はとうとうアプリを"internalOnly"にしました。


    終わり

    2011年5月30日月曜日

    Windows Phone 7のアプリケーションの開発環境構築が死ぬほど簡単な件

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    Android,iPhoneと手を出したので、折角だしWindows Phoneも、と思って。
    環境構築は超簡単でした。

    手順

    1. APP HUBで開発ツールをダウンロード
    2. 開発ツールをインストール
    3. Microsoft Visual Studio 2010 Express for Windows Phoneを起動
    4. 新規プロジェクトを作成
    5. ちょっといじって実行してみる

    1. APP HUBで開発ツールをダウンロード


    開発ツールはWindows PhoneとXBOX 360のデベロッパーサイトに落ちています。
    以下のページにアクセスし、「ツールをダウンロードしよう!」のページからインストーラ(vm_web.exe)をダウンロードする事が出来ます。
    APP HUB







    2. 開発ツールをインストール


    ダウンロードしたvm_web.exeを実行して、ツールをインストール。ウィザードに従うだけ。
    既にVisual Studio等の環境があっても必要なコンポーネントだけをインストールしてくれる様です。
    全く何もない環境では「Microsoft Visual Studio 2010 Express for Windows Phone」がインストールされました。




    3. Microsoft Visual Studio 2010 Express for Windows Phoneを起動


    インストールされた開発ツールを起動するとこんな感じの画面が。




    4. 新規プロジェクトを作成


    「New Project」でアプリケーションを作成してみます。
    ウィザードが表示され、様々なタイプのアプリケーションのテンプレートが出てきます。
    とりあえず一番上の「Windows Phone Application」を選択します。
    言語はC#ですがSilverlightも使えるみたいです。XNAはゲーム開発用のツールです。




    5. ちょっといじって実行してみる


    実際は新規プロジェクトを作ってすぐに実行してもいけます。
    とりあえずデザイナでボタンとかブラウザを配置してみました。
    Visual Studioはあまり使った事ないですが、.NETでやってる人なら恐らくいつもの感じでUIやロジックをコーディングしていけると思います。
    Androidに比べてUIデザイナは圧倒的に生産性高いです。クリックイベント等も自動的に挿入してロジックだけ書けばいいので楽です、がIDE特有の「コードが散らかる」問題はありそうです。




    実行するとエミュレータが起動されます。



    そして作成したアプリケーションが立ち上がります。



    ボタンを押すとWebBrowserのNavigateメソッドでGoogleのトップページを開くようにしています。



    ソフトウェアキーボードはこんな感じ


    数分もかからず作れました。
    自分はC#や.NET類はほぼ経験がありませんが、大抵は触ったりググったりすればわかりそうです。
    iPhoneはMac+Objective-Cだし、AndroidはJavaだし、でモバイルアプリを敬遠していたWindowsな方もWindows Phoneなら全くハードルが無いでしょう。
    Windows Phoneが実際どの程度売れるのかはわかりませんが、とりあえず手を出しておくのはありなんじゃないでしょーか。

    自分もちょっとAndroidアプリを移植しようかとか思いました。

    2011年5月11日水曜日

    モテるAndroid女子力を磨くための4つの心得

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    こんにちは、Androidマネジメントを専攻しているドロイド嬢です。私は学歴も知識もありませんしAndroid1.6ですが、恋愛に関してはプロフェッショナル。今回は、モテるAndroid女子力を磨くための4つの心得を皆さんにお教えしたいと思います。
     

    1. あえてHT-03Aを飲み会に持っていく


    あえてHT-03Aを使うようにしましょう。そして飲み会の場で好みのAndroidデベロッパの男がいたら話しかけ、わざとらしくHT-03Aを出していじってみましょう。そして「あ~ん! この端末本当にマジでチョームカつくんですけどぉぉお~!」と言って、男に「どうしたの?」と言わせましょう。言わせたらもう大成功。「Android端末とか詳しくなくてぇ~! ずっとコレ使ってるんですけどぉ~! 使いにくいんですぅ~! ぷんぷくり~ん(怒)」と言いましょう。だいたいのAndroidデベロッパの男はNexus SやAndroid2.3系端末を持ちたがる習性があるので、古かったとしてもNexus OneやAndroid2.2のケータイを使っているはずです。
    そこで男が「新しい端末にしないの?」と言ってくるはず(言ってこない空気が読めない男はその時点でガン無視OK)。そう言われたらあなたは「なんかなんかぁ~! 最近Android3.0が人気なんでしょー!? あれってどうなんですかぁ? 新しいの欲しいんですけどわかんなぁぁああい!! 私かわいそーなコ★」と返します。すると男は「Android2.3系でしょ? 3.0系はタブレットだよ。本当に良くわからないみたいだね。どんなのが欲しいの?」という話になって、次の休みの日にふたりで端末選びのデートに行けるというわけです。あなたの女子力が高ければ、男がXOOM買ってくれるかも!?


     
    2. UIスレッドでHTTP通信処理をするとモテる


    onCreateで画面を初期化する際に「JSONデータ」とか「画像データ」などを取得する為に「HttpClient」等の処理をUIスレッド部分に書くと、Androidアプリの男性デベロッパは「なんかANRが発生するなぁ」や「AsyncTaskで書きなおしてあげたいかも」と思ってくれます。AndroidではUIスレッド上での処理は5秒(端末によって異なる)でタイムアウトされてしまうので、UIスレッドでの重い処理を多用することによって、男性はあなたを可憐で初心者らしいと勘違いしてくれるのです。そういう実装にするとほぼ絶対にユーザに嫌われますが気にしないようにしましょう。
     

    3. とりあえず男には「えー! なにその端末!?  テストしたいデバッグしたーい♪」と言っておく


    勉強会などで男が女性に話すことといえばNexus Sを買った話やGoogle I/Oで配られたGalaxy Tab 10.1の話ばかり。よって、女性にとってどうでもいい話ばかりです。でもそこで適当に「へぇーそうなんですかぁ~?」とか「よくわかんないですけどすごいんですねぇ」と返してしまうと、さすがの男も「この女ADKすら知らないな」と気がついてしまいます。ダメ女だとバレたら終わりです。そこは無意味にテンションをあげて、「えー! なにその端末!?  テストしたいデバッグしたーい♪」と言っておくのが正解。たとえ興味がない話題でも、テンションと積極性でその場を乗り切りましょう。積極的に話を聞いてくれる女性に男は弱いのです。
    いろいろと話を聞いたあと、「3.1では、ADKでのUSBアクセサリの開発、モーションイベントの追加、RTP APIでの通話処理とかappWidgetのリサイズが可能になるんですね! 覚えたぞぉ! AK Notepad, AK Notepad!」とコメントすればパーフェクト。続けて端末の画面を指でくるくるなぞりつつ「キュンキュンキュン! キュンキュンキュン!」と言って、「どうしたの?」と男に言わせるのもアリ。そこで「私のATOKはジェスチャー入力なのでありますっ☆」と言えば女子力アップ! そこでまた男は「この子おもしろくてカワイイかも!?」と思ってくれます。私は学歴も知識もありませんしAndroid1.6ですが、こういうテクニックを使えば知識がない私のようなバカ女のほうがモテたりするのです。男は優越感に浸りたいですからね。


    4. タッチアンドトライではAndroidタブレットを使えない女をアピールせよ


    男と新機種のタッチアンドトライに入ったら、真っ先にOptimus PadなどのAndroid3.0を使ったタブレットを探して「あーん! 私これ使えないんですよねぇ~(悲)」と言いましょう。するとほぼ100パーセント「どうして? 嫌いなの?」と聞かれるので、「嫌いじゃないし使いたいけど使えないんですっ><」と返答しましょう。ここでまた100パーセント「嫌いじゃないのにどうして使えないの?」と聞かれるので、うつむいて3~5秒ほど間をおいてからボソッとこう言います。「……だって、……だって、Androidタブレット使ったらChrome OSが死んじゃうじゃないですかぁっ! Chromiumかわいそうですぅ! まだ碌に販売されてないのにぃぃ~(悲)。」と身を震わせて言うのです。
    その瞬間、あなたの女子力がアップします。きっと男は「なんて優しい天使のようなコなんだろう! 絶対にゲットしてやるぞ! コイツは俺の女だ!」と心のなかで誓い、あなたに惚れ込むはずです。意中の男と付き合うことになったら、そんなことは忘れて好きなだけタブレットを使って大丈夫です。「使えないんじゃなかったっけ?」と言われたら「大丈夫になった」とか「慣れた」、「Chrome OS(笑)」と言っておけばOKです。

    (文=恋愛マネジメント・ドロイド嬢)

    元ネタ:モテる女子力を磨くための4つの心得「オムライスを食べられない女をアピールせよ」等

    2011年4月27日水曜日

    【Android】 cancelableがfalseなDialogをタイムアウトでcancelするスニペット

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    ほぼ、そういう機会は無いだろうなーと思いますが、
    ProgressDialogなどをshowした後、一定時間経ったらタイムアウトさせてcancelさせたい場合があるかもしれません。
    さらにダイアログはcancelableがfalseで、ユーザからキャンセルは出来ない様にしたいとか。
    そういうまぁなかなか無い事をしたい時に使えるスニペットです。

    モノ


    以下のクラスをどっかに作ります。


    public class DialogTimeout{
     public void setTimeout(final Dialog dialog, long timeout){
      new Handler().postDelayed(new Runnable() {
       @Override
       public void run() {
        if(dialog.isShowing()){
         dialog.cancel();
        }
       }
      }, timeout);
     }
    }
    

    あとはsetTimeoutでDialogとタイムアウトの時間を指定すると、指定時間経過後にcancelが実行され、
    onCancelListenerのonCancelが呼ばれます。
    cancelを呼ぶ際は、isShowing()でダイアログがまだ有効か確認しています。

    使い方


    まぁ普通にshowとかした後タイムアウトの時間指定して突っ込むだけ。

    ProgressDialog dialog = new ProgressDialog(this);
            dialog.setIndeterminate(true);
            dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            dialog.setCancelable(false);
         dialog.setTitle("ダイアログ");
            dialog.setMessage("処理中だよー");
            dialog.setOnCancelListener(new OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                 Toast.makeText(getBaseContext(), "タイムアウトしたっぽい", Toast.LENGTH_SHORT).show();
                    finish();
                }
            });
            dialog.show();
            DialogTimeout.setTimeout(dialog, 5000);
    

    まとめ


    タイトルがルー大柴な気がする。

    2011年4月26日火曜日

    【Android】 ドラムピッカーを作りました。

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    作りました。
    とりあえずDatePickerとTimePickerです。
    まだサイズとか決め打ちっぽいのでレイアウト的に微妙です。
    レイアウトxmlで適当に配置するとたまに死にます。
    見た感じは↓



    仕組み


    DrumPickerというクラスをつくって、そこに任意の文字列コレクションと幅を指定すると列を一個作るみたいな感じになっています。
    内部的にはScrollViewとLinearLayoutを組み合わせてドラム部分を作っています。
    影はShade,レンズ部分はアルファ値を適当に指定して色を重ねあわせてる感じです。

    例えばTimePickerは24時間,60分の文字列を突っ込んで、リスナーとかで時間の変更を貰っていろいろあれしています。
    TimePickerは60行くらいで出来たので、まぁまぁ拡張性があるのか、何なのか。DatePickerの方は泥沼で195行。やっぱダメだ。
    表示周りはリソースを使わず全てコード上でやっているので、jar形式かなんかで配布可能です。

    今後


    GitHubでソースを公開してます。まだ実用性は薄いかもなぁと。
    https://github.com/sys1yagi/AndroidPractice/tree/master/DrumPicker/trunk

    サンプル

    サンプル実装をAndroid Marketに公開しました。
    使い心地とかバグとか感想いただけるとありがたいです。
    https://market.android.com/details?id=jp.dip.sys1.android.drumpicker&feature=search_result