‘JavaScript’ タグのついている投稿

apple-itunes-appと同等のものをAndroidで実現する

iOS 6のSafariではapple-itunes-appというメタ要素をHTMLのヘッダに指定することで、ブラウザ上部にアプリへのリンクを表示させることができます。

これをAndroidでも簡単にGoogle Play Storeへのリンクを表示できるJSがありました。

jQuery Smart Banner – Jasny · web development

jQueryのライブラリとして公開されています。

    <meta name="apple-itunes-app" content="app-id=544007664">
    <meta name="google-play-app" content="app-id=com.google.android.youtube">

こんな形で設定できるようです。


Android開発で3G回線速度を再現する

ネットワークを利用するAndroidアプリを開発していると、回線速度が遅い場合の処理を気にしない訳にはいかなくなります。

開発中はWi-Fiで接続しているから気付かなかった問題が、SIMの入った実機では頻発するなんてこともあります。

通信の内容確認と、速度の制限のために、PCのProxyを経由して通信するようにします。Wi-FiのProxy設定はICS(4.0)から可能です。

設定済みのネットワークであれば、長押しして設定画面を表示します。

image

プロキシ設定を手動にして、ホスト名にPCのIPアドレスを入力します。

image

PC側にはFiddlerを入れます。

Fiddler側の設定はこちらにまとめています。
Androidエミュレータの通信をFiddlerで見る : blog.loadlimit – digital matter –

Android実機からFiddler経由で接続できるようになったら、速度制限を試してみます。

メニューのRulesから、Performance→Simulate Modem speedsにチェックを入れると、「モデムの回線速度」をエミュレートできます。

WS000027

ただ、モデムなので遅くて最近のネットワークとしては使い物になりません。動画ダウンロードに数時間かかるとかザラです。

ここから速度を調整します。

Rules→Customize Rulesを開きます。デフォルトではメモ帳でJSファイルが開きます。

image

request-trickle-delayという行を探します。

		if (m_SimulateModem){
			// Delay sends by 300ms per KB uploaded.
			oSession["request-trickle-delay"] = "300";
			// Delay receives by 150ms per KB downloaded.
			oSession["response-trickle-delay"] = "150";
		}

ここで1KBごとに実行する遅延時間を設定することができます。request-trickle-delayがアップロードの遅延時間、response-trickle-delayがダウンロードの遅延時間です。数字が小さいほど高速になります。

デフォルトの設定ではアップロードが1KBで300msの遅延なので、3.33KB/s、27.3kbpsくらいです。ダウンロードは54.6kbpsくらいです。1024*(1000/150)*8/1000=54.6133..(kbps)ですね。

例えば好意的に見て3Gが1500Kbpsで上下通信できるとすれば、delay値は5.33、小数を切り捨てて「5」としておきます。5msの遅延ではあまり違いがわからないかもしれないので、僕は単純にそれぞれ30と15にしています。

編集が終わったらJSファイルを保存して閉じます。これで遅いネットワークが再現できます。

Fiddlerを使えば、他にもAutoResponder機能で、レスポンスを全部定義してサーバ代わりにしたり、通信自体にブレークポイントを設定してネットワークが応答しなかった場合の処理の確認も可能です。

追記(2013/06/13)

エミュレータの場合はnetspeedオプションをつけることで、速度を指定できます。

emulator.exe -netspeed umts

とすれば、3G回線速度がエミュレーションされます。

その他の速度についてはこちら。

Using the Emulator | Android Developers

ディレイも設定できるので、

emulator.exe -netdelay umts -netspeed umts

という感じで起動すれば良いかと思います。


node.js用のglob

PHP的にglobコマンドでファイル一覧を取得したかったので書いてみました。

var
  fs = require('fs');

fs.glob = function (path, pattern, callback) {
  fs.readdir(path, function (cb) {
    return function (err, files) {
      if (!err) {
        var files2 = [];
        for (var index in files) {
          if (files[index].match(pattern)) {
            files2.push(files[index]);
          }
        }
        cb(err, files2);
      }
      else {
        cb(err, files);
      }
    }
  } (callback));
};

fs.glob('.', /.*\.log/i, function (err, files) {
  console.log(files);
});

こんなんでいいのかな。一応動いているっぽいです。

[ 'npm-debug.log',
  'service.log',
  'test.20120621.log',
  'test.20120621090035.log',
  'test.20120621090058.log',
  'test1.log',
  'test1.old.log',
  'test2.log' ]

Cordova(PhoneGap)でAndroid開発する際の勘所

PhoneGapの練習も兼ねてアプリひとつ作りました。

ヨルニンゲン (Jorningen) ※現在公開中のバージョンはJavaに書き直しました

https://play.google.com/store/apps/details?id=info.play_smart.android.Jorningen

device-2012-05-21-143445

生活時間帯が標準時間とズレている人が、自分の中では今何時なのかを把握するための時計アプリです。
例えば明け方6時に寝る人は、朝6時を夜12時のつもりとして登録しておくと、自分時間を表示しておけます。
分単位で、自分の中での今の時間を入力できるところが、世界時計とはちょっとコンセプトの違うところです。
アプリ名は夜人間ですが、昼人間も使えます。

で、AndroidアプリでPhoneGapを使ってみたのでハマりそうな部分やポイントを書いておきます。ちなみに使ったバージョンは1.7.0です。xui.jsとデータの保存にlawnchairを使いました。

Apache Cordova

PhoneGapのAndroid版は、名前がCordovaになったので検索時など注意しましょう。2011年のセットアップ記事とか見るとハマるかも。

ダウンロードはこちらから。ここからダウンロードできるのはgithub上にあるtarballです。
PhoneGap

思い切りが必要

結構思い切らないとPhoneGapを使うのは難しいです。Javaが必要な部分はJavaで書いて連携させればいいとか、思わないほうがいいです。すべてJavaScriptでやる気持ちでやりましょう。

なるべくJavaを使わない

双方の呼び出しは何とかなりますが、オブジェクトの共有はしにくいです。

OptionMenuは諦める

メニューボタンを押したときの挙動をJava側に書くことはできますが、メニューを出し分けたければJSの状態を常にJava側にフィードバックしておかないといけなくなります。

x$(document).on("menubutton", func);でJS側で完結させましょう。

代わりに設定ページに遷移させる

ページ遷移は、一枚のHTMLの中に複数のdivを作っておいて、切り替えて使うのが良さそうです。以下のサンプルが参考になると思います。

https://github.com/alunny/phonegap-start

上記サンプルのlawnchairはちょっとバージョン古いので、気をつけてください。

sqlite returned: error code = 14, msg = cannot open file at source line 25467

lawnchairを使っていて、このエラーが表示されたりしますが、動作に支障はないと気付くのに結構時間を使いました…

開発環境的な面では楽

Chromeとデベロッパーツールでほぼ開発できるので、実機への転送は画面の確認程度です。

ただし、chromeではdevicereadyイベントが起きないので、その辺の処理が必要なら開発時用と実機用で分ける必要はあります。

xui.jsは機能的にはかなり弱い

http://phonegap.com/tools

http://xuijs.com/

xui.jsがよく使われるようですが、機能はかなり少ないです。そのかわり、軽いです。どうも自分でextendして使うような感じです。見た目に関してはサポートしてくれないので、その辺の手間もかけたくない場合はSenchaTouchとかjQuery Mobileとかの方がいいと思います。

多言語化しにくい

https://github.com/phonegap/phonegap-plugins/tree/master/Android

Globalizationプラグインは地域とか言語を取得してくれるだけなので、文章を差し替えたりはできません。

assetsからindex.htmlを読み込む際にoncreateで切り分けるのも可能ですが、文章だけ変えたindex.htmlのコピーを作ることになるのでちょっと更新が手間になります。

でも多分values/string.xmlとか使ってしまうとブラウザでの開発がやりにくくなるので、JSのフレームワークの機能に頼るのがベターではないかと思います。

JSでnavigator.languageを取得しても英語にしかならない

さっき多言語化しにくいと書きましたが、言語を切り分けようとしてnavigator.languageを参照してもenしか返ってきません。

http://groups.google.com/group/phonegap/browse_thread/thread/f6b6ba2021ee7fb4/239e7ecb71619579

上記URLの方法でUserAgentから言語を取り出すようです。

ちなみにアプリの起動中では切り替わらないようで、アプリの強制終了→起動をしないと言語設定が有効になりません。

戻るボタンでアプリから抜けても、プロセスが終了しない

ひとつ上の言語切り替えとセットで少しハマりました。これは別にいいのかな。

Ripple使うと少し楽

Chromeの拡張のRipple Mobile Environment Emulatorをインストールすると、画面サイズを端末ごとに合わせてくれたり、位置情報のエミュレーションをしてくれたりします。

Chrome ウェブストア – Ripple Mobile Environment Emulator (Beta)

ローカルのファイルを読ませるためには、プラグインにローカルファイルへのアクセスの許可をする必要があります。

Chromeの拡張機能を管理→Ripple→アイコン左の三角をクリックして詳細を表示→ファイルのURLへのアクセスを許可するにチェック、でOKです。

でも画面サイズがうまく一致しなかったです。謎。

それと、スタンドアローン版もあるのですが、ローカルファイルの指定の仕方がわからず、結局使ってません。それと、バージョンが微妙に古いです。

機能とか使い方とか、別エントリでまとめようと思います。

ネットワークのパーミッションが必要

ネットワークを使わなくても、android.permission.ACCESS_NETWORK_STATEが必要です。これがないとSecurityExceptionが発生します。

他のパーミッションは全部消しても大丈夫です。

結論

結論としては、簡単なアプリならいいかな、という一般的な感想ですが、とりあえずJSのフレームワーク選定はものすごく重要です。自由にレイアウトできる軽量な画面遷移フレームワークがあったらいいかな、と。

あとはHTML+CSSを組むのにやたら時間がかかってしまったので、これを何かGUIのエディタでどうにかすべきだと思いました。Dreamweaverとかでもいいですが、もっとCSSの知識不要で作りたいところです。


html5uploaderで複数アップロード時のバグ修正

Gmailのようなドラッグアンドドロップでファイルをアップロードする機能が使いたかったので探していたところ、見つけたのがhtml5uploaderです。JSと、サーバーサイドのPHPが付いてます。

html5uploader

デモ
http://www.weeby.pl/blog/html5uploader/uploader.html

比較的簡単な構造で最低限のアップロード機能を実装しているので、elFinderと組み合わせて使うことにしました。

で、表題のバグですが、複数のファイルを同時にアップロードすることはできるのですが、時々サーバからNo file to uploadというメッセージが返ってくることがあります。リクエストを見ると、送るはずのContent-Lengthが0バイトで、データを送信していないようです。

原因はonloadendイベントの関数でreaderオブジェクトを直接呼び出して使ってるからですね。というわけで修正。

// Once the process of reading file
this.loadEnd = function() {
	bin = reader.result;

// Once the process of reading file
this.loadEnd = function() {
	bin = event.target.result;

に修正して完了です。

Pluploadの方が高性能かつソースもきれいでよく出来てるのですが、GPLなんですよね。コマーシャルライセンスは10ユーロなんで良心的で安いんですが。今回はファイルマネージャーに統合しちゃいたかったので、小型のhtml5uploaderにしてみました。

elFinderがDnDのアップロードに対応しないかなぁ。


リバースProxy環境でのmixiアプリのOAuth

CakePHPでmixiアプリのOAuthを処理させる方法は以下のサイト参照。ホントにシンプルでわかりやすい解説。

[cakephp] mixiアプリのOAuthのリクエストを受け取る – 「のーぶるじゃすぱー」略して「のぶじゃす」のBLOG

通常の環境ではこのまま実装すればOK。

で、このあとロードバランサーの入ったサーバに持ってきたらOAuthが正しく認証されない。

OAuthRequestのhttp_urlを見てみると、

http://192.168.0.2:8001:80//api/hoge/hoge/…

という悲しいことになっていて、これがどうも問題のようだと。というか、ホスト:ポート:ポートってどういうことだ…

本来ならもちろん、

http://mixiapp.sample.com:80/api/hoge/hoge/…

みたいなアドレスになっていないといけない。

ちなみに送信側はmixiアプリのサンプルそのまんまな感じのJavaScript

var params = {};
params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = 0;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
var url = "http://mixiapp.sample.com/api/hoge/hoge/";
gadgets.io.makeRequest(url, func, params);

解決策は

function validateOAuth () {

    $get = $_GET;
    unset($get['url']);
    $http_url = 'http://mixiapp.sample.com:80/' . ltrim($_SERVER['REQUEST_URI'],'/');
    $request = OAuthRequest::from_request(null, $http_url, array_merge($get, $_POST));
    if ($_GET['oauth_consumer_key'] == 'mixi.jp') {
        @$signature_valid = $this->MixiAppliOauth->check_signature($request, null, null, $get["oauth_signature"]);
    }

    if ($signature_valid == true || Configure::read('debug')) {

        $this->oauth_valid = true;
        $this->viewer_id   = $get['opensocial_viewer_id'];
        $this->owner_id    = $get['opensocial_owner_id'];

        return $get;

    }

    return false;

}

というように、from_requestの第二引数にURLを指定してやればOK。なぜかスラッシュが2つ付いていたので削って付けなおしてます。


iPhoneのSafariでtouchendイベントを使うときの注意

iPhone用のドラッグアンドドロップで操作できるWebアプリを作っていて、うっかりはまったtouchendの挙動についてのメモ。

最初に書いたコードはこんな感じでした。

function touchhandler(e) {
    var x = e.touches[0].pageX;
    switch (e.type) {
        case 'touchstart':
            break;
        case 'touchmove':
            break;
        case 'touchend':
            break;
    }
}

document.getElementById('sample').addEventListener("touchend", touchhandler, false);

このプログラムではtouchendの処理が実行されません。

というのもまぁ、気づけば簡単なことなんですが、最後の指をスクリーンから離してtouchendのイベントが発生するときは、もうスクリーンに触ってないのでe.touches[0]がundefinedになるんですね…

なので、touchendのときは、引数で受け取ったe.touchesは注意しましょうという話。

今回参考にさせていただいたのは以下のサイト。

flashcast:フリーで働くITエンジニア集団のブログ: ipod touch用のWeb Applicationを作成してみる(canvasでお絵かき編)

SitePen

Web開発参考サイト – iPhone 3G DevWiki


Pythonのwin32comからSilverlightを使おうと試みる

最終的にはPythonで書かれたWindowsアプリケーションで、Silverlightのコンテンツをロードしたいという目的なのですが、とりあえずSilverlightのActiveXコントロールを読み込んで動作を確認してみたいので色々実験。

まず、インストールされているSilverlightのバージョンを調べてみる。

Javascriptで書かれたコードがいくつか見つかったのでそれを書き直す。

以下はJS版。

var control = new ActiveXObject('AgControl.AgControl');
alert(control.IsVersionSupported('3.0'));

これをPythonで書いてみる。

import win32com.client
control = win32com.client.Dispatch("AgControl.AgControl")
print control.IsVersionSupported('3.0')

Silverlight 3がインストールされていればTrueが表示されます。

他にどんなメソッドが定義されているのかを調べるために、Visual StudioからAgControlを参照して、オブジェクトブラウザで調査。

というか、Silverlightのプラグインのリファレンスがあった。

Silverlight プラグインのオブジェクト リファレンス

が…Sourceを指定してもIsLoadedプロパティがTrueにならない…うーん…もう少し調べてみる。