Saturday, November 23, 2013

Angularjsでcode上でpropertyをrefreshする

AngularJSを使うとHTML上のng-modelを指定したelementとjsコード上で$scope.hogeで指定したプロパティを自動的に同期してくれるのですが、コード上でプロパティを変更しても、そのままでは同期してくれません。

っていうところでいささかはまった話です。

なにやら以下のように書く必要があるようです。




$scope.$apply(function () {
    $scoep.anyProperty = 'hogehoge';
});

$applyの引数には式を文字列としても渡せるみたいです。

Thursday, November 21, 2013

socket.ioを使ってimageをuploadする

画像なんてsocket越しにアップロードするものかわかりませんが。
expressjs + socket.ioでsocketごしに画像をサーバーに送信 > サーバー側でそれを画像として保存後、URLをクライアントに配信 > それをまた画面で受け取って表示するというサンプルを書いて実験してみました。

ポイントとしては
FileReader#readAsDataURLでDataURI形式にした画像を文字列としてサーバーに送る


         var file = input.files[0];
         var name = file.name;

         var reader = new FileReader();
         reader.onloadend = function (e) {
             var data = e.target.result;

             if (!data) {
                 return ;
             }

             socket.emit('upload', { name : name, data : data});
         };

         reader.readAsDataURL(file);


サーバー側で文字列的にbase64でencodeしてある部分を取り出して画像としてファイルに出力
                var imgStructure = img.split(',');
                var base64 = imgStructure[1];

                fs.writeFile(
                    './public/' + name,
                    new Buffer(base64, 'base64'),
                    function (err) {
                        console.log(err);
                    }
                );


その後はクライアント側で関しているイベントにたいして出力した画像のパスを配信
socket.emit('notify', {url : publicUrl});


クライント側で、受け取った画像パスをimgタグに突っ込んで表示
     socket.on('notify', function (data) {
                   console.log(data);
                   $('#image001').attr('src', data.url);

               });


実際に作成したサンプルは下においてあります。
https://github.com/hiroyukimizukami/socketio-image

Tuesday, November 12, 2013

RubyOnRailsとiOSでアプリを作る時にすばらしく便利なNSRails

Rails + iOSという組み合わせは割と多いと思うんだけど。いちいちサーバーとクライントの繋ぎ込みを自前で書くのはつらいなーと思って調べたら、NSRailsという便利なのがあったので試してみた。

https://github.com/dingbat/nsrails

Railsのモデルと対になるものをiOS側で定義しておくとiOSのオブジェクトを操作するだけサーバにデータを永続化したり、データの取得ができる。ざっくりいうとparse.comと非常に使用感が似ている。

導入


導入はcocoapods経由で。Podfileにuse 'NSRails'とかくだけ。
pod 'NSRails'

iOS側でRailsアプリの場所を指定するには、AppDelegateで以下のように指定するだけ。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    
    [NSRConfig defaultConfig].appURL = @"http://localhost:3000";
    
    return YES;
}

Railsアプリ


アプリと適当なオブジェクト+エンドポイント一式を作っておく
rails new nsrails_test_server
rails g scaffold user name:string email:string passwd:string created_at:datetime updated_at:datetime

あと、RailsアプリにCSRF対策用のキーをチェックする設定が入っているので/app/controllers/application_controller.rbのprotect_from_forgeryを:exceptionから:null_sessionに変更し無効とする。

protect_from_forgery with: :null_session


iOSアプリ

Railsのモデルに対応するモデルをクライアントとに定義する。NSRRemoteObjectを継承したクラスを作成。プロパティ名はRails側と合わせておく。


#import "NSRails.h"

@interface JpPluscNSRailsTestUser : NSRRemoteObject

@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSString* email;
@property (nonatomic, strong) NSString* passwd;
@property (nonatomic, strong) NSString* gender;

@end

iOS側のオブジェクトはプレフィックスの都合とかでRailsとあわせられない事が多いと思うので、そういう場合は実装ファイルでremoteModelName, remoteControllerNameを上書きする
+ (NSString *) remoteModelName
{
    return @"user";
}

+ (NSString *) remoteControllerName
{
    return @"users";
}

@end


あとはオブジェクトの作成、永続化、取得等が以下のように簡単にかける。
    //オブジェクト作成、永続化
    user = [[JpPluscNSRailsTestUser alloc] init];
    user.name = self.aName.text;
    user.email = self.aEmail.text;
    user.passwd = self.aPasswd.text;
    
    [user remoteCreateAsync:^(NSError *error) {
        if (error != nil) {
            NSLog(@"%@", error);
        }
    }];

    //取得
    [JpPluscNSRailsTestUser remoteAllAsync:^(NSArray *allRemote, NSError *error) {
        NSMutableArray *indexPaths = [NSMutableArray array];
        NSInteger size = [allRemote count];

        //画面レンダリングなど...        

    }];


実際に書いたコード iOS
Rails

Tuesday, August 27, 2013

apkの中身を見る

dex2jar を使ってapk > jarに変換

jarをJava Decompilerで開く

と見れる。便利。

Proguard+parse.comのAndroidSDKの話

Parse.comのAndroidSDKを使ったアプリを開発して、最後にさあProguardかけて公開するぞって時にWarningsが出まくってantでビルドかからなかったり、apkは作れたんだけど難読化したらSDKの中で落ちたりしてはまったのを解決した話


ant release -> [proguard] Warnings:com.parse.... が出まくってbuild failedな時

-dontwarn com.parse.** 

で、黙殺


ビルドはなんとか通ってapkは出来たが、実行時にSDKの中で落ちる時
sdkのコードは難読化しなくてよさそうなので
-keep class com.parse.**

で、Proguardの対象から外す


作成したapをdex2jarとJava Decompilerを使って解凍して内容を確認したところ開発分のソースは難読化されていたので大丈夫そうだった。

Monday, August 5, 2013

Android4.3変更点まとめ

Android - Android 4.3, Jelly Beanの変更点のまとめ

* RestrictedProfile. 家庭で家族と、職場で、アプリとコンテンツに制限付きでアクセスできる
* Bluetooth Smartのサポート. Androidにフィットネスセンサーに接続する全ての新しい種類のアプリへの準備をさせる
* OpenGL 3.0のサポート


Audio
* 仮想surround sound対応

DialPad
* 発信番号オートコンプリート(オプトイン導入

Keyboard & Input
* タップタイピングの認識アルゴリズムが改善されて入力が簡単に。
* ゲームパッドやジョイスティックのための入力の応答が早くなった

Location
* Wifi経由の位置特定 - いちいちWifiをonにしなくてもWifi経由の位置特定ができるように。


Networking
* BluetoothSmartReadyサポート
* Bluetooth AVRCP 1.3サポート ... カーステレオで曲名を表示できるように。

Graphics

* OpenGL ES 3.0
* WirelessDisplay ... タブレットの映像をTVで移せる(Nexus7(2013) Nexus10)

Internationalization and localization

* 多言語サポート、対象言語の追加
* 右から左げ書く言語の表示サポート

Settings

* Disabled apps tab ... 設定からどのアプリが無効化されているか確認する事ができるようになった

System

* Restricted profiles  ... アプリやコンテンツへのアクセスに制限がかかった状態を作ることができるようになった
* セットアップウィザードが簡単になった
* スクリーンロック状態からユーザーを切り替えるのが早くなった
* フォトのデイドリーム機能がさらに改善された

もうちょっとデベロッパ向けのページもあったので、そちらは後ほど。。
http://developer.android.com/about/versions/jelly-bean.html

Monday, July 29, 2013

AndroidStudioが「Guilding XXX Gradle project info」で止まる

前回、AndroidStudioの起動まで意味もわからずできたが、その後プロジェクトの新規作成で「Guilding XXX Gradle project info」というポップアップが出て以降進まなくなった。

ログを見てみると相変わらず「WARN - .intellij.util.EnvironmentUtil - timed out」が出ている。

EnvironmentUtil#L132で環境変数$SHELLをとってから「-l -c /usr/bin/printenv」をしている。
$SHELLが/bin/bashをさしてたけども、そこを普段使っているzshをさすように.zshrcに「SHELL=/bin/zsh」とかして環境変数を変更したら無事プロジェクト作成まで完了した。

今回もよくわからず。。


Tuesday, July 23, 2013

Android TextView get/setGravityの指定方法

TextViewの位置揃えに使うget/setGravityの記述方法でややはまった。

文字を左寄せにしようとしてsetGravity(Gravity.LEFT);とか書いた後にgetGravityすると最初に指定した値と全然違うものが帰ってくるのだ。色々原因を調べてみたのだが、どうやら、segGravityには水平、垂直方向両方の指定がなされる事を前提としているらしい。なのでsetGravityには水平、垂直方向両方の指定をするか、getGravity時にマスクをかけて任意の方向の値指定のみを取得する必要があるらしい


//Gravity.Leftのnumeric表現は3
textView.setGravity(Gravity.LEFT);
final int gravityValue = textView.getGravity();
//gravityValue に3を期待するが実際は51が帰ってくる

//正しくは以下のように水平、垂直方向両方の指定をする
textView.setGravity(Gravity.LEFT|Gravity.TOP);

//もしくはgetGravityした値に対して任意の方向のマスクをかけると垂直、水平方向のみの値が取得できる
final int horizontalGravityValue = textView.getGravity & Gravity.HORIZONTAL_GRAVITY_MASK; //水平方向の値指定のみ取得
final int verticalGravityValue = textView.getGravity & Gravity.VERTICAL_GRAVITY_MASK; //垂直方向の値指定のみ取得


TextView#L2816(バージョン4.2.2)
ソースを眺めたら、setGravityでは垂直方向の指定がなされていない場合はGravity.TOP, 水平方向の指定がなされていない場合はGravity.STARTを自動的に指定する模様。

Sunday, July 21, 2013

Android タッチ時に対象Viewに枠線をつける(特殊編)

前提:
今回取り扱う問題は以下のちょっと特殊なViewで発生した話です。通常のViewでは発生しない問題かと思います。
* 自前で拡張してドラッグ&ピンチズームアウトされる
* ドラッグやピンチに従って自領域と座標を再描写し続ける

AndroidでViewをタッチしたときに色を変えたり枠線をつけたりというのはよくある話で、selectorを用意してitemのstate_pressedとかでshapeを書くような感じになると思うのですが、今回はitemのandroid:state_xxxで対応仕切れないタッチイベントの制御をしたかったのでそれらxml群は捨て、Viewを拡張して直接onDrawをいじる事にした。


private static final BACKGROUND;
private static final BORDER;
static {
    BACKGROUND = new Paint();
    BACKGROUND.setColor(Color.argb(192, 255, 0, 0));
    BORDER = new Paint();
    BORDER.setStyle(Paint.Style.STROKE);
    BACKGROUND.setColor(Color.WHITE);
}

@Override
protected void onDraw(Canvas c) {
    super.onDraw(c);
    c.drawRect(0, 0, c.getWidth(), c.getHeight(), BACKGROUND);
    c.drawRect(0, 0, c.getWidth(), c.getHeight(), BORDER);
}


素直にこんな感じのコードを書いたのですが、いざ動かしてみるとゆっくりドラッグしたとき不規則に枠線のゴミが残る。。(昔のWindowsでマシンの応答が悪いときにウィンドウをグリグリしたときみたいな感じ)

で、該当Viewの領域はドラッグに合わせて絶えず移動しているので、移動によって枠線が自分の領域の外になってしまい、そこの部分の再描写がかからずにゴミが残ってるのではという仮説の元


@Override
protected void onDraw(Canvas c) {
    super.onDraw(c);
    c.drawRect(0, 0, c.getWidth(), c.getHeight(), BACKGROUND);
    c.drawRect(1, 1, c.getWidth()-1, c.getHeight()-1, BORDER);
}

枠線を無理矢理Viewの領域の中に入れてみたらうまくいった。快適。

Android Studioが起動しない

ローディングの画面で止まったり、その次のWelcome to Android Stuioの画面で固まったして全く進まなかったのが進むようになった話。結論から書くと僕の場合は普段zshなのだけどbashからApplications/Android Studio.app/Contents/MacOS/studioを叩いたら動いた、全く意味が分からない。

前提:
OSX
AndroidStudio AI-130,737875
JDK 1.6.0_33

WARN - .intellij.util.EnvironmentUtil - timed out
どうにもこうにも進まないので~/Library/Logs/AndroidStudioPreview/idea.logを確認したところ、起動直後にこのログが出てた。


https://android.googlesource.com/platform/tools/idea/+/934b9431b0b827a132df794e307fe5a2b70de00b/platform/util/src/com/intellij/util/EnvironmentUtil.java
でちょっとソースを追ってみると、ここのL134のコマンドが帰ってきてなくてプロセスをtimeoutさせてるらしき事が判明。

手元で該当コマンドを叩いても結果は帰ってくるものの中身が全くからだったので何となくbashから叩いてみると結果がちゃんと帰ってきた。そこで、もしかしたらbashからAndroidStudio叩けば動くかなーと思って最初の展開。全く意味が分からないがとりあえず動いた。





Monday, July 1, 2013

クリーンコードその2の1

記憶する要素が少ないコード

コードを読むときは式や条件を一つ一つ頭の中のスタックに詰め込んでいく事になりますが、一度に頭の中に覚えておける要素って何個くらいなのでしょう。体感では5−6個程が限度かなという気がします。あまり多いと最初に読んだ条件式などがすっぽ抜けて泣く泣く最初から読み直したりする事になるわけです。なので、頭の中に覚えておかなければならない事が少ないコードは良いコードだと思います。

ガード条件を使って例外を速攻切り捨てる

以下は、恣意的な例ですがDVDレンタルのお店で、ある人物がDVDを借りられるかを判断するな処理です。このような書き方だと、ifに該当しなかったデータがそのまま捨てられるのか、またはelse文が出現して別の式によって処理されるのかほぼすべての条件を舐めないと決定できません。今取り扱っているデータと、コード中のほぼすべての式を頭の中でスタックしないと読めないコードであり、最初の1スタックが飛んだりすると一から読み直しになってしまいます。また、メイン処理(この場合会員にDvdを貸し出す処理)が膨大な条件の中に埋もれていて、大事な処理が全く目立たないコードでもあります。

if (aPerson != null) {
    if (aShop != null) {
        if (dvd != null ) {
            if (aShop.isKnowingPersonAsMember(aPerson.getId()) {
                DatingSlip datingSlip = null;
                 if (aDvd.isXrated()) {
                    if(aPerson.isAdult()) {
                        datingSlip = aShop.lend(aPerson, aDvd);
                    } else {
                        throw new PermissionDeniedException();
                    }
                } else {
                     datingSlip = aShop.lend(aPerson, aDvd);
                }
                aShop.addDatingSlip(datingSlip);
            } else {
                throw new PermissionDeniedException();
            }
        } else {
            throw new NoSuchDvdException();
        }
    } else {
        throw new IllegalArgumentException();
    }
} else {
    throw new IllegalArgumentException();
}

この処理はガード条件を使って業務例外ケースをどんどんはじいていく事でかなりシンプルに出来ます。メイン処理に不適格なデータはその場で捨てられるため、そのステップ以降で、その条件について気にする必要がなく頭の中の記憶量もごく少なく済みます。


if (aPerson == null || aShop == null) {
    throw new IllegalArgumentException();
}

if (aDvd == null) {
    throw new NoSuchDvdException();
}

if (aShop.isKnowingPersonAsMember(aPerson.getId()) {
    throw new PermissionDeniedException();
}

if (aDvd.isXrated() && !aPersion.isAdult()) 
    throw new PermissionDeniedException();
}

DatingSlip datingSlip = aShop.lend(aPerson, aDvd);
aShop.addDatingSlip(datingSlip);

Wednesday, June 26, 2013

クリーンコードその1

前から後ろへ上から下に順を追って読めるコード

プログラムを読むとき頭の中は基本的にスタック構造になってると思います。なので、頭の中のスタックのソートや順番の入れ替えが内容にプログラムを書くと読みやすくなると思います。

制御分は前におく

my $hoge;
$hoge += $_ foreach (@huga);
return $hoge if ($hoge % 2 == 0);

perlなどでは、if forなど後置制御文があります。この書き方はステートメントが出現した後にその式を司る制御分が出てくるため、一度頭にステートメントをスタックした後でその前に制御分を差し込む必要が出てしまいます。このせいで、一度認識している処理の流れを捨てて新しい処理の流れの理解する必要が出てきます。なのでステップ数は多くとも制御分は前置きにした方が自然な流れでコードが読み進められると思います。

my $hoge;
foreach (@huga) {
    $hoge += $_;
}
if ($hoge % 2 == 0) {
    return $hoge;
}

map, grepは単発にする

my @even_score = grep { 
    $_ % 2 == 0 
} map { 
    $_->score 
} grep {
   not $_->is_viewer($viewer)
}@user_score;
JSとかのmap, grepは大丈夫なのですが、perlのmap, grepはそれがつながっている場合、前半の処理が後続のmap, grepの結果に依存するため、一度読んでいる箇所からmap, grepのチェーンが終わるところに飛びそこから逆に読み進めていく事となるせいで、読む流れが途切れてしまいます。こちらもステップ数が増えてでも1つの処理毎に一時変数に値を取って書くのが良いと思います。

my @other = grep { not $_->is_viewer($viewer) } @user_score;
my @value = map { $_->score } @huga;
my @even_score = grep { $_ % 2 == 0 } @value;