Techracho

ACTION_SCREEN_ONを受け取る(2)

このエントリーをはてなブックマーク Share
2010.05.31    android, java, 馬場      baba   

ACTION_SCREEN_ONを受け取るの続きです。

ACTION_SCREEN_ONを受け取るには、コードで明示的にregisterReceiverする必要がありました。

しかし、AppWidgetは表示時に生成されたあと、すぐに終了してしまいます。
コンストラクタでログを吐けば、onUpdate()とonDeleted()が呼ばれたときでは、それぞれでコンストラクタが呼ばれて、別のインスタンスが生成されるのが確認できると思います。

したがって、onUpdate()でregisterReceiverしてしまうと、終了時にunregisterReceiverする手段が無くなってしまうため、そのままでは使えません(どんどんReceiverが増えていきます)。

これを解決するには、サービスを使えば良さそうです。

たとえば以下のようなサービスを作っておいて、

public class ScreenStateService extends Service {
	private BroadcastReceiver mReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
				sendBroadcast(new Intent("jp.bpsinc.ScreenStateService.SCREEN_ON"));
			} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
				sendBroadcast(new Intent("jp.bpsinc.ScreenStateService.SCREEN_OFF"));
			}
		}
	};

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		getApplicationContext().registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
		getApplicationContext().registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
	}

	@Override
	public void onDestroy() {
		getApplicationContext().unregisterReceiver(mReceiver);
		super.onDestroy();
	}
}

AppWidgetProviderのonUpdate()で、このサービスを起動します。

@Override
public function onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    context.getApplicationContext().startService(context.getApplicationContext(), ScreenStateService.class));
}

ウィジェットの終了時には、忘れずにサービスを止めておきます。

@Override
public void onDeleted(Context context, int[] appWidgetIds) {
    context.getApplicationContext().stopService(context.getApplicationContext(), ScreenStateService.class));
    super.onDeleted(context, appWidgetIds);
}

こうしておけば、マニフェストファイルで指定することで jp.bpsinc.android.SCREEN_ON などのイベントを受信できます。

このままだと複数ウィジェットに対応していないので、カウンタ方式などでもう少し工夫した方が良いですが・・・

ACTION_SCREEN_ONを受け取る

このエントリーをはてなブックマーク Share
2010.05.30    android, 馬場   タグ: , —    baba   

ホームスクリーンウィジェット(AppWidget)開発では、省エネ対策が重要になります。

画面がOFFの時は、無駄な処理を省きたいものです。

リファレンスによると
・ACTION_SCREEN_ON : “android.intent.action.SCREEN_ON”
・ACTION_SCREEN_OFF : “android.intent.action.SCREEN_OFF”
というのが定義されているので、これを受信すれば対策できそうです。

http://developer.android.com/reference/android/content/Intent.html

しかし、単にmanifest.xmlのreceiverに書いても、受信してくれません。

ダメな例:

<application android:icon="@drawable/icon" android:label="@string/app_name">
  <receiver android:name="jp.bpsinc.android.test.WidgetProvider">
    <intent-filter>
      <action android:name="android.intent.action.SCREEN_ON" />
      <action android:name="android.intent.action.SCREEN_OFF" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider" android:resource="@xml/provider" />
  </receiver>
</application>

SCREEN_ONなどを受け取るには、アプリの初期化処理で、コードから登録する必要があります。

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
  IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
  context.getApplicationContext().registerReceiver(new BroadcastReceiver() {
    @Override
      public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "RECEIVE");
    }
  }, filter);
}

この際、
・渡されたcontextではなくApplicationContextを使うこと
・AppWidgetProviderもonReceive持っているからいけそうな気がするが、receiverにthisを指定してはいけない
がポイントです。
守らないと、以下のようなエラーが発生します。

android.content.ReceiverCallNotAllowedException: IntentReceiver components are not allowed to register to receive intents

Providerにまとめて処理させたいなら、以下のようにやればOKです。

public class WidgetProvider extends AppWidgetProvider {
  @Override
  public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
    context.getApplicationContext().registerReceiver(new BroadcastReceiver() {
      @Override
        public void onReceive(Context context, Intent intent) {
          WidgetProvider.this.onReceive(context, intent);
      }
    }, filter);
  }

  @Override
  public void onReceive(Context context, Intent intent) {
    //なんたらかんたら
  }
}

ただ、単にこの方法を使うと、Receiverが無限に増えていってしまいます。
アプリを終了してもReceiverが残ってしまうので、サービスにbindしないと実質的にはダメですね。

Android AppWidgetで使えるコンポーネント

このエントリーをはてなブックマーク Share
2010.05.29    android, java, 馬場      baba   

BPS Android Libraryにも、かなりたくさんのクラスが溜まってきました。

ImageViewなどを継承した便利なクラスもいくつかあるのですが、AppWidget開発の際に使ったら ClassNotFoundException が発生してしまいました。

理由は単純で、AppWidgetでは使えるコンポーネントが制限されていて、ImageViewなどのサブクラスは使えないです。
完全に見落としていました。

http://developer.android.com/guide/topics/appwidgets/index.html

A RemoteViews object (and, consequently, an App Widget) can support the following layout classes:

FrameLayout
LinearLayout
RelativeLayout
And the following widget classes:

AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView

Descendants of these classes are not supported.

リファレンスはしっかり読みましょう。

MusicFlyにスペインからフィードバックをもらいました

このエントリーをはてなブックマーク Share
2010.05.28    MusicFly, android, プロダクト, 馬場   タグ: , —    baba   

好評を頂いているMusicFlyに、スペインの方からフィードバックを頂きました。

Androidマーケットからのコメントは、英語のフィードバックが一番多いです。

むしろ日本語が少数派なのですが、スペイン語はたぶん初めてでした。

5カ国語くらい話せる某社長は、本日も大忙しです。

MusicFlyは、来週最新版がリリースされるので、もう少しお待ちください!
数々の新機能を搭載しています。

※配信アカウントが変更になる予定です。発表の際はQRコードを掲載しますので、よろしくお願い致します。

Windowsで消せないファイルを削除

このエントリーをはてなブックマーク Share
2010.05.27    Windows, 馬場      baba   

Windowsではファイルが消せないトラブルが良くありますね。

代表的なのは別プロセスが使用している場合ですが、ファイル名が不正になっているのが原因のこともあります。

http://support.microsoft.com/kb/320081/ja

今回は、Macで圧縮したファイルを文字化けせずに展開できるExplzhで解凍したファイルが、削除できなくなりました。

文字コードの関係で、フォルダの最後にスペースが入ってしまったのが原因です。

こうなってしまったらExplorerで削除する方法はないので、コマンドプロンプトで

rd /s "\\?\C:\Users\baba\Desktop\hoge "

のように、ダメなフォルダを指定します。

ファイルの場合は、rd /s の代わりに del を使えばOKです。

サーバで動いているWordPressをローカルに再現する

このエントリーをはてなブックマーク Share
    PHP, 伊藤      tomotaka   

伊藤です。

サーバで動いているWordPressをローカルで再現して、テーマを編集したいときのやりかたを紹介します。
ローカルはWindows+XAMPPを想定していますが、基本的にapacheとmysqlを使っているならとくにどんな環境でもいけそうです。

  1. ファイル一式をサーバから持ってくる。これはtarで固めてscpなどでよいでしょう。
  2. サーバのデータベースデータを持ってくる。mysqldumpやphpMyAdminのエクスポートなどを使いましょう。
  3. ローカルのMySQLにデータベースを作る(wp-config.phpを参考に)
  4. ローカルのMySQLにユーザを作る(wp-config.phpを参考に)
  5. ローカルのMySQLにサーバのデータベースデータを流しこむ。 mysql -u root < serverdata.sql
  6. ローカルのphpMyAdminでwp_optionsテーブルのsiteurl, homeを編集する(ローカルで運用したいURLに変更)
  7. ローカルのApacheでDocumentRootやAliasを設定する

基本的にはこれでOKでした。

コマンド作業などをもうすこし補足しておきます。

サーバ上のファイルを固めてダウンロード

$ sudo tar cvzf /home/myself/blog.tar.gz /path/to/blog

blog.tar.gzのダウンロードはWinSCPなどを使うとよいでしょう。

サーバのデータベースデータをもってくる

$ mysqldump -u root wordpress > /home/myself/blog.sql

MySQLのrootパスワードをきちんと設定している場合は-pオプションをつけて入力します。
MySQLのrootを使えない場合はwp-config.phpを参考に、wordpress用ユーザでDBを指定して出力させます。

$ mysqldump -u USER -p DATABASE > /home/myself/blog.sql
Enter password: (PASSWORD)

blog.sqlのダウンロードはWinSCPなどを使うとよいでしょう。

ローカルのMySQLにDB, ユーザを作る

コマンドプロンプトでxamppのmysqlを起動しましょう。
この例ではmysql.exeまでの絶対パスを記述していますが、環境に合わせてこのパスを書き換えてください。

> C:\Users\tomotaka\Documents\xampp\mysql\bin\mysql -u root
mysql> CREATE DATABASE `(DB名)`;
mysql> GRANT ALL ON `(DB名)`.* TO '(ユーザ名)'@localhost IDENTIFIED BY '(パスワード)';
mysql> \q

これでデータベースとユーザができましたね。
あとはサーバ上の記事データを流し込みましょう。

ローカルのMySQLにデータを流し込む

> C:\Users\tomotaka\Documents\xampp\mysql\bin\mysql -u root < C:\Users\tomotaka\Documents\blog.sql

簡単です。

phpMyAdminでwp_optionsを編集

  1. phpMyAdminに入る
  2. wordpressのDBを選択する(流しこみが成功してればある)
  3. wp_optionsのテーブルを選択して、表示する
  4. option_nameカラムがsiteurlの行のエンピツマークをクリックして編集モードになる
  5. サーバで運用してたブログのアドレスが入っているが、ローカルで使うアドレスに書きかえる
  6. option_nameカラムがhomeの行のエンピツマークをクリックして編集モードになる
  7. サーバで運用してたブログのアドレスが入っているが、ローカルで使うアドレスに書きかえる

こんなかんじ。

以上です!
みなさんの快適なWordPress生活のお役に立てば幸いです。

RailsのActiveRecordで日付処理

このエントリーをはてなブックマーク Share
2010.05.26    Ruby, Ruby on Rails, 馬場   タグ: —    baba   

RailsでDBにdatetime型のカラムがある場合、特に意識しなければ、DBにはUTC時刻が保存されます。

ActiveRecordでデータを取得すると、ActiveSupport::TimeWithZone 型のオブジェクトが取得できます。
http://www.51773.com/tools/api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html

to_sやinspectをすればそれっぽい文字列になってくれますが、日本人なら、ちゃんとフォーマットして欲しいですよね。

to_sにフォーマットを渡せるようになっているので、

obj.date.to_s(:db)

と指定すると、2010-05-01 10:00:00 のような形式で取得できます。
ただ、これだとUTC時刻のままになるので、

obj.date.localtime.to_s(:db)

のようにやると良さそうです。

また、日本語の形式などに変換したいときは、

Time::DATE_FORMATS[:jp] = "%Y年%m月%d日 %H時i分s秒"

のように指定すれば良いみたいです。

http://japan.zdnet.com/blog/yoshimi/2008/04/22/entry_27016455/

RailsのHash.from_xmlに注意

このエントリーをはてなブックマーク Share
2010.05.25    Ruby, Ruby on Rails, 馬場   タグ: —    baba   

RailsのActiveSupportは大変便利で、生Ruby使うときも

irb -r rubygems -r active_support

をデフォルトにしたくなります。

# 個人的には .blank? が一番便利だと思います。

ところで、Hash.from_xmlを使うとお手軽にXMLをパースできますが、若干癖があるので注意が必要です。
・子要素も属性も同じように扱われる
・同じ名前の要素が複数あると自動で配列になる
・typeという名前の属性は、無視されることがある
・ハイフンはアンダースコアに置換される

たとえば、user-listの中にuserが複数ある場合、

{"user_list" => {"user" => ["yamada", "tanaka"]}}

のように変換されるため、扱いやすいのですが、userがたまたま1件だと、

{"user_list" => {"user" => "yamada"}}

のようになり、userが配列と期待しているプログラムは動かなくなります。
特に、検索系のXMLを使う際は注意が必要です。

また、属性は子要素のように使えますが、属性が1つだけだったり、子要素が文字列の場合、扱いが変わったり消えてしまうことがあるので、注意しましょう。

以下に例を挙げます。

<a><b size="123">456</b></a>
=> {"a" => {"b" => "456"}}

<a><b size="123" /></a>
=> {"a" => {"b" => "123"}}

<a><b type="123" /></a>
=> {"a" => {"b" => nil}}

<a><b size="123"><c>456</c></b></a>
=> {"a" => {"b" => {"size" => "123", "c" => "456"}}}

<a><b type="123"><c>456</c></b></a>
=> {"a" => {"b" => {"c" => "456", "type" => "123"}}}

複雑なXMLをパースする際は、ちゃんとnokogiriなどのライブラリを使うと良さそうですね。

分散バージョン管理Bazaarのチュートリアル(Eclipse対応!)

このエントリーをはてなブックマーク Share
    Eclipse, 伊藤   タグ: , , , —    tomotaka   

こんにちは。伊藤です。

今日はアプリケーション開発とは切っても切り離せないバージョン管理システムについて書こうと思います。
BPSでは現在バージョン管理システムとしてSubversionをメインに使用していますが、
ここ数年の分散バージョン管理システムの盛り上がりから、
分散バージョン管理システムについて最近調査・検証しています。

ある共同プロジェクトでGit, Mercurialとならぶ分散バージョン管理システム3強のうちの1つ、Bazaarを試す機会があったので、
EclipseでBazaarを使うところまでを共有したいと思います。

Bazaarのインストール

Bazaarのインストールはとても簡単です。

Bazaar公式「Download and Install」のページから自分の環境を選んで、インストーラをダウンロードします。
または説明に沿ってインストールします。

WindowsもBazaarプロジェクトがインストーラを用意しています。
MacOSX用もdmgがありましたので、インストーラが用意されているものと思います。

WindowsではTortoiseBZRというTortoiseSVNのようなシェル統合もインストールされるようですが、
2010/05時点でTortoiseSVNほどの完成度はないようです。

Windowsの場合インストールが終わったら環境変数PATHが更新されているので、いったん再起動するとコマンドラインにもパスが通ります。
bzrコマンドのオプションなどは環境に依存せず同じですので、安心して使えますね。

ローカルにリポジトリを作って使ってみる

windowsでも、UNIX系OSでも、適当にディレクトリを作成して、そこにcdして
bzr init
と打ってみます。
これだけでそのディレクトリは既に立派なBazaarリポジトリです!

ここにtest.txtというファイルを作ってみましょう。
名前や内容はなんでもいいですが…

$ echo "test" > ./test.txt

この状態で、いまBazaarでどういう状態かというのを訪ねるにはbzr statusコマンドを使います。
省略してstatでもいけます。

$ bzr stat
unknown:
  test.txt

こんな感じになるはずです。
これは現在bazaarで管理されてないファイルtest.txtがあるよ、という意味です。
test.txtをBazaarに管理させましょう。svnと同じように、addしてcommitです。

$ bzr add test.txt
adding test.txt
$ bzr commit -m "added test.txt by tomotaka"
Committing to: /home/tomotaka/code/test/
added test.txt
Committed revision 1.

こんな感じになるはずです。
add, stat, commitの他にもmv, log, mkdir, remove, remove-tree, diff, revertなどのコマンドがあります。
だいたいsvnなんかと同じですよね。

詳しいコマンドのリファレンスは

bzr help
bzr help COMMAND

などで調べることができます。

もちろんBazaar公式のオンラインリファレンスを見るのもいいでしょう!

これで一つのディレクトリをバージョン管理することはできるようになりましたね。
次のセクションではネットワークのむこうにあるリポジトリのやりとりを行う実践編です!

リモートのサーバからリポジトリをbranch

ここでは前提として、リモートサーバ上にプロジェクト全員で共有するリポジトリがあり、
最終的な変更はすべてそこに集約しようという前提で話を進めます。
(ようするにSubversionの作業フローを維持してどうBazaarでモアベターに置き換えるかということです。)

どうやら分散管理システムでは、プロジェクトを複数人で共同開発する際は個人ごとにリポジトリを持つのが普通のようなので(そうしないと分散の意味がない)、
checkoutではなく、branchコマンドでリモートにあるリポジトリの複製を作りましょう。(gitならgit cloneかな?)
リモートサーバへはbzr+sshプロトコルで接続するのが簡単そうです。
sshのサービスがあがっていればなにも設定しなくても使えます。便利ですね。

$ bzr branch bzr+ssh://tomotaka@honyarara.bpsinc.jp/path/to/repository/ ./localbranch

これで自分のデスクトップで作業を行うことができますね。
このlocalbranchに対するコミットは自分だけのコミットになるので、
自分の中でのひと区切りや、一定機能単位などで好きにコミットできます。
ここが分散バージョン管理システムの利点です!
subversionなどの集中型のバージョン管理システムでは、
(とくにメンバーの多いプロジェクトでは)リビジョン番号の爆発を防ぐためや、トラッキングのしやすさを維持するために
ある程度ローカルでまとめあげてから(バグを取り除いて、完全に動く状態にする)コミットするのが慣例かと思います。

しかし、これのよくないところは「バグが取り除かれて、完全に動く状態になる」までの一切の変更はバージョン管理されていないのです!
つまり「バグが取り除かれて、完全に動く状態」にならない限り、昨日のコードに戻したい
とかができないわけですね。上記のポリシーだと。

分散リポジトリなら、自分のリポジトリにガンガンコミットしても、誰にも迷惑はかかりませんし、
むしろバージョン管理ができるのでガンガンコミットするべきです。
適切な単位=バグが取り除かれて、完全に動く状態になったら、それをまとめてリモートのリポジトリに反映してあげましょう。

リポジトリ間での差分をやりとりするには、pushとpullというコマンドを使います。
pushは自分の変更履歴を引数で指定されたリポジトリに送ります。
pullは指定したリポジトリの変更履歴を取り寄せて自分のリポジトリに反映します。

リポジトリは互いに対等ですが、Bazaarではコピーしたリポジトリは自分のコピー元の
リポジトリのURLを覚えているため、リポジトリを指定しないbzr pushを実行すると
コピー元リポジトリにpushしてくれます。これがsvnでいう中央サーバへのコミットのようなイメージですね。

$ bzr push

うーん、簡単。

となりの人のマシン(192.168.0.123)の、となりの人のリポジトリから中央サーバを介さずに
となりの人が施した変更をもらってくることもできます。

$ bzr pull bzr+ssh://tomotaka@192.168.0.123/tonari/no/hito/no/repository

これでネットワーク経由でのバージョン管理もマスターできました!?

bzr-eclipseを使ってみる

BPSではいろいろな言語をプラグインを利用して統一的なインタフェースで編集することができるEclipseを
共通の開発環境として使っています。
普段はSubclipseプラグインを使ってサーバ上のSubversionリポジトリとやりとりを行っているため、
似たような使い勝手のEclipseプラグインを探してみたところ、
どうやらBazaarにもbzr-eclipseというEclipseプラグインがあることがわかりました。

ただ、このプラグインは単独では動かず、bazaarが実行するPCにインストールされていないといけません。
先にBazaarをインストールしましょう!
あ、Bazaarだけでなくbzr-xmloutpuptプラグインが必要でした。
Windowsのインストーラは標準でbzr-xmloutputを入れてくれます。
Ubuntuだったらaptitude install bzr-xmloutputでインストールできます。

インストールは簡単で、いつものeclipseのプラグインインストーラにURLを入れればいけます。
最新バージョンがアップロードされているURLは、Bazaar公式のbzr-eclipse intallationページから参照してください。
うまくインストールできましたでしょうか。
インストールできたら設定画面のTeam > Bazaarから、Bazaarのバイナリ(bzr.exe)を選択する必要があります。
Windows 7ではインストーラのデフォルトのままインストールしたらC:\Program Files (x86)\Bazaar\bzr.exeでした。
環境に合わせて適切に設定しましょう。

しかしこれだけではbzr+sshプロトコルでリモートサーバとうまくやりとりすることができません。
とくにWindowsでは設定がめんどくさいです!(Linux版Eclipseは普段使わないのでここでは触れられません, すいません!)
bzr+sshをうまくいかせるためにいろいろ悪戦苦闘したので、ここで共有します。

まず、PuTTYで鍵交換方式によるSSH接続のページを参考に、
コマンドプロンプトで

$ bzr branch bzr+ssh://目的のリモートリポジトリURL/

を実行してパスワードが出なくなるところまで持っていきます。
おおざっぱに書くと

  1. puttygen, pagentをDLする
  2. puttygenでprivate keyとpublic keyをつくる, パスフレーズはなしで。
  3. pagentをdouble clickで起動, タスクトレイにいる帽子かぶってるやつ右クリックでadd key => つくったprivate key登録
  4. 公開鍵をサーバに転送。 ssh-keygen -i -f (公開鍵ファイル) >> authorized_keysで変換してauthorized_keysに追加
  5. コマンドラインでbzr branch bzr+ssh://user@host/path/to/repos/ localrepos/ とかやって認証なしでbranch切れるか確認する
  6. うまくいけばbzr-eclispeでもいけるはず。(File > New > Project > Bazaarにbranch作成がある)

ですね。

普段サーバ作業をあまりしない方には以下のチェックリストが参考になるかもしれません:

  • 公開鍵をauthorized_keysに登録するときに変換し忘れてない?
  • .sshディレクトリのパーミッションは700 ? オーナーは自分 ?
  • authorized_keysのパーミッションは600 ? オーナーは自分 ?
  • ファイル名間違ってない ?
  • 公開鍵と秘密鍵間違えてない ?

コマンドプロンプトでうまくいけば、bzr-eclipseでもうまくいきます。
bzr-eclipseでリモートリポジトリからbranchをつくるには、new project > Bazaarから行います。
Importではないので注意!

—-

いかがだったでしょうか?
以上で今回のBazaarチュートリアルはおしまいです。

それではBazaarで楽しい開発ライフを!

XAMPPでRailsのProxy

このエントリーをはてなブックマーク Share
2010.05.24    Ruby, ネットワーク, 馬場   タグ: , , —    baba   

Railsで開発する際は、素直にRadRailsのボタンでサーバを起動してしまうのが楽で良いです。
例:http://localhost:3000

しかし、ポート番号ではなく好きな名前でアクセスしたいこともあります。
また、Rails 2.3には、prefix(ドメイン直下以外にアプリを配置)にバグがあるため、それをローカルで試したいこともあるかもしれません。

XAMPPだけで普段はサーバ作業に触れない人には馴染みが薄いかもしれませんが、Apacheなので、たんなるProxyならかんたんです。
例:http://localhost/myapp

<VirtualHost *:80>
	ProxyRequests Off
	<Proxy *>
		Order deny,allow
		Allow from all
	</Proxy>
	ProxyPass /myapp http://localhost:3000
	ProxyPassReverse /myapp http://localhost:3000
</VirtualHost>

なお、肝心のPrefixバグの対策は、システムチームの伊藤がブログを書いていますのでご覧下さい。
http://www.bpsinc.jp/blog/archives/1579

古い投稿 »

COPYRIGHT [C] 2009 BEYOND PERSPECTIVE SOLUTIONS LTD. ALL RIGHTS RESERVED.