2012年10月30日火曜日

Nexus 7(32GB)の日本での販売開始。Nexus 10は11月13日から。Nexus 4は日本販売予定なし。

Nexus 4, 7, 10が発表された。ハリケーン「サンディ」上陸の影響で発表会が開けなくて、Googleは残念だったろう。

以下は発表された販売スケジュール。ちなみに、Nexus 4とNexus 7(32GB)3G対応モデルの、日本での販売予定は今のところ無い



どちらもOSは4.2(Jelly Bean)。

8GBは$299、16GBは$349。米国、英国、オーストラリア、フランス、ドイツ、スペイン、カナダのGoogle Play Storeで11月13日から販売開始。

Nexus 4はLTEが使えないのがミソ。



OSは4.1(Jelly Bean)。

16GBのWiFiモデルは、既に日本でも販売されているが、今日から32GB WiFiモデルの販売も始まった。価格は24,800円


Nexus 7(32GB)3G対応モデル by ASUS

OSは4.1(Jelly Bean)。

$299でアンロックされて販売される。米国、英国、オーストラリア、フランス、ドイツ、スペイン、カナダのGoogle Play Storeで11月13日から販売開始。



どちらもOSは4.2(Jelly Bean)。

16GBは36,800円、32GBは44,800円で、11月13日から日本で販売開始。その他、米国、英国、オーストラリア、フランス、ドイツ、スペイン、カナダのGoogle Play Storeでも11月13日から販売開始。

Android 4.2(Jelly Bean)の特徴まとめ

Android 4.2搭載のNexus 4, 7, 10が発表された。そこで、4.2で新しく加わった機能などをを少しまとめてみた。


・コードネームはJelly Beanのまま

4.1から変わらないのね。


・Photo SphereでJPEG画像がストリートビューのように見られる

今回のOSで一番売りにしているポイントっぽい。

JPEG画像のメタデータの中にXMLを含めることで、静止画像なのに上下左右など、撮影した画像の周辺が360度見られる。
Hugo Barra - Google+ - Photo Sphere: capturing the world around you We just…

4.2でJPEG画像を開けば、なんでもそのように見られるわけではなく、4.2搭載の端末のカメラで撮影した画像のみ対応する。


・ユーザーの切り替えができるようになった(タブレット限定機能)

MacやWindowsなどのデスクトップOSのように、マルチユーザーで1端末が使用される、ホームユースを考えて搭載した機能だろうね。

ユーザー毎に領域が分けられているので、ホームスクリーン、壁紙、ウィジェット、アプリ、ゲームのハイスコアなども、個人ごとに管理される。ログアウトせずに、別のユーザーに切り替えることも可能。

これはタブレット限定の機能で、4.2搭載でもフォンタイプではこの機能は使えないみたい。


・フォンからタブレットへ動画や写真をワイアレスで映し出す

動画や画像をもうちょっと大きく見たいという時に、HDMI対応のTVや4.2搭載タブレットへダイレクトで表示内容を映し出すことが出来る。

家族が集った時や結婚式などのイベントで有効に使えるかも?


その他の情報は Android - What's New で確認できる。

2012年10月29日月曜日

YouTube動画をダウンロード可能にする機能はTOS違反。・・・だよね?

YouTubeの動画をダウンロードさせるアプリケーションがApp Store等で幾つも公開されている。中には有料のアプリだってある。これってアリなの?と普段から気になっていたので調べてみた。


Terms of Service - YouTube — Google Developers

I. API Use
Compliance with Terms.
You may use the YouTube API, and the data, content, and information made available through the YouTube API ("API Data"), in connection with a website or software application ("API Client") developed by You only in accordance with the terms and conditions of this Agreement, the YouTube Terms of Service (currently located at http://www.youtube.com/t/terms), the YouTube API documentation and specifications (currently located at http://code.google.com/apis/youtube/overview.html), and the developer credentials and API Client IDs assigned to you by YouTube.


「開発したWebサイトやアプリケーションで、YouTube APIを通してデータ、コンテンツなどの情報を扱いたい場合は、YouTubeの利用規約、YouTube APIのドキュメント・仕様、開発者証明、YouTubeのAPI Client ID(YouTubeによって割り当てられたID)への合意によってのみ使用を許可される。」意訳するとこんな感じか。


そして、YouTubeの利用規約の「5. お客様によるサイト上のコンテンツの使用」の項目Bには、ダウンロードを許可しない記述がある。

本コンテンツは、「現状有姿」でお客様に提供されています。お客様は、本コンテンツに、本サービスの機能により意図されており本サービス条件により認められている限度で、参考目的での個人的な利用のためにのみアクセスすることができます。お客様は、「ダウンロード」または同様のリンクが本コンテンツについて本サービス上でYouTubeにより表示されている場合を除き、いかなる本コンテンツもダウンロードしてはなりません。

「本サービス上でYouTubeにより表示されている場合」はダウンロードはOK、ダウンローダーのようなソフトウェアのダウンロードリンクはダメ、という所まで書いてある。動画のオーナーだったら自分がアップした動画にはダウンロードボタンが付いているが、それのみOKということだろう。


ダメ押しで、FAQにもちゃんと書いてある。

YouTube 動画をダウンロードできますか?

いいえ。YouTube 動画をサイトからダウンロードまたはそれを試みるサービスを提供しているウェブサイトは数多くあります。しかし、コンテンツの提供時に YouTube によって「ダウンロード」リンクまたはそれに類似するリンクが表示されている場合を除き、YouTube 動画をダウンロードすることや外部のアプリケーションを使用して動画をダウンロードすることは、動画所有者の著作権を侵害し YouTube の利用規約に違反する行為です。


やっぱり、YouTube動画をダウンロード可能にする機能はYouTubeのTOS違反だ。

でも、そんなアプリでもApp Store等の審査で弾かれないのが不思議。

App Store等にしてみれば、自らのTOSを遵守しているかは確認するが、そのアプリが利用しているAPIまでの遵守確認は行わない、そこまでは知ーらない、ということか。


ダウンロードできると便利なシーンはいろいろあるが・・・。でもTOSに反するようなアプリをユーザーさんには使わせられない。

自分がアプリを作るときにはTOSに反しないように意識しておく。

2012年10月27日土曜日

GCMのIntentServiceでAsyncTaskを実行したらsending message to a Handler on a dead threadの警告が出て悩まされる

GCMサーバーからメッセージが送られてきた時の処理をGCMIntentServiceのonMessage()へ記述している。
my.app.GCMIntentService
  |- com.google.android.gcm.GCMBaseIntentService
      |- android.app.IntentService

onMessage()内でAsyncTaskクラスを作成してexecute()したところ、以下のようなWarningが出た。
10-26 15:24:14.025: W/MessageQueue(482): Handler{44f63ea8} sending message to a Handler on a dead thread
10-26 15:24:14.025: W/MessageQueue(482): java.lang.RuntimeException: Handler{44f63ea8} sending message to a Handler on a dead thread
10-26 15:24:14.025: W/MessageQueue(482):  at android.os.MessageQueue.enqueueMessage(MessageQueue.java:179)
10-26 15:24:14.025: W/MessageQueue(482):  at android.os.Handler.sendMessageAtTime(Handler.java:457)
10-26 15:24:14.025: W/MessageQueue(482):  at android.os.Handler.sendMessageDelayed(Handler.java:430)
10-26 15:24:14.025: W/MessageQueue(482):  at android.os.Handler.sendMessage(Handler.java:367)
10-26 15:24:14.025: W/MessageQueue(482):  at android.os.Message.sendToTarget(Message.java:348)
10-26 15:24:14.025: W/MessageQueue(482):  at android.os.AsyncTask$3.done(AsyncTask.java:214)
10-26 15:24:14.025: W/MessageQueue(482):  at java.util.concurrent.FutureTask$Sync.innerSet(FutureTask.java:252)
10-26 15:24:14.025: W/MessageQueue(482):  at java.util.concurrent.FutureTask.set(FutureTask.java:112)
10-26 15:24:14.025: W/MessageQueue(482):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:310)
10-26 15:24:14.025: W/MessageQueue(482):  at java.util.concurrent.FutureTask.run(FutureTask.java:137)
10-26 15:24:14.025: W/MessageQueue(482):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
10-26 15:24:14.025: W/MessageQueue(482):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
10-26 15:24:14.025: W/MessageQueue(482):  at java.lang.Thread.run(Thread.java:1096)

コードのスニペット。
@Override
 protected void onMessage(final Context context, Intent intent) {
  Log.i(TAG, "Received message.");
  // IMAGE_IDを取得
  String imgId = intent.getStringExtra(IMAGE_ID);
  
  // Async version
   GetImgTaskTest getImgTaskTest = new
   GetImgTaskTest(context.getApplicationContext());
   getImgTaskTest.execute(imgId, null, null);
 }


StackOverFlowで、このWarningの回避策が触れられていた。

Issue 20915 - android - AsyncTask can get initialized with wrong Looper - Android - An Open Handset Alliance Project - Google Project Hosting

そこでは、onCreate()内でAsyncTaskクラスをロードしておくやり方が紹介されていた。
public class YourApplication extends Application {

    @Override
    public void onCreate() {

        // workaround for http://code.google.com/p/android/issues/detail?id=20915
        try {
            Class.forName("android.os.AsyncTask");
        } catch (ClassNotFoundException e) {
        }

        super.onCreate();
    }
}


これはAsyncTaskクラスのバグだという言及も。

android - onPostExecute not being called in AsyncTask (Handler runtime exception) - Stack Overflow

This is due to a bug in AsyncTask in the Android framework. AsyncTask.java has the following code:

private static final InternalHandler sHandler = new InternalHandler();

It expects this to be initialized on the main thread, but that is not guaranteed since it will be initialized on whichever thread happens to cause the class to run its static initializers. I reproduced this issue where the Handler references a worker thread.


しかし、このClass.forName()を使う方法は、あまりエレガントに思えなかったので、AsyncTaskのスレッドルールについて調べてみた。

AsyncTask | Android Developers "Threading rules" より
・The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
・execute(Params...) must be invoked on the UI thread.

AsyncTaskのAPIドキュメントにも、UI threadでクラスをロードせよ、execute()はUI threadで実行せよと、しっかり書いてある。


ナゼ?


まず基本的なことから。


・AndroidのUIは基本的にsingle threadモデルであることを理解すべし

AndroidのUIは基本的にsingle threadモデル。全てのアプリは1つのプロセスとスレッドで実行される。そのスレッドのことを別名"main" threadとよんでいる。

UI threadの中で、ネットワーク通信やデータベース検索など、時間がかかる処理を実行した場合、レスポンスが悪くなったり、アプリが止まっているように見えたりしてしまう。よって、以下の2点だけは守ろう。

Processes and Threads | Android Developers "Threads"より
Thus, there are simply two rules to Android's single thread model:

1. Do not block the UI thread
2. Do not access the Android UI toolkit from outside the UI thread


・時間がかかる処理はworker threadで行い、main threadから分けるべし

即座に応答しなくてよい操作だったら、それらは別のスレッド(バックグラウンドやworker threads)で処理できないか確認しよう。

Processes and Threads | Android Developers "Worker threads"より
If you have operations to perform that are not instantaneous, you should make sure to do them in separate threads ("background" or "worker" threads).

で、worker threadの結果は、以下の様なメソッドやクラスを使って反映させよう。



分かった!Warningの理由

そう、つまり、AsyncTaskは、worker threadがUI threadとコミュニケートしながら処理を進めるという操作を、簡単に書けるように作られたクラスなのだ。だから、execute()はUI thread内で実行することを想定している。

AsyncTaskのClass Overviewにも、真っ先に書いてある。

「AsyncTaskはUI threadを適切に、そして簡単に使えるようにしたもの。このクラスを使うと、ThreadやHandlerを使って自分で色々せずに、処理をバックグラウンドで行い、その結果をUI threadへ表示させることができる。」
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread.


ここまで整理すると、Warningが出ていた理由も理解できる。

UI threadで実行するルールになっているAsyncTaskを、IntentService内で、つまりworker thread内でexecute()したていたからだ。

IntentSreviceではAsyncTaskは使えない。

非同期処理と思うとAsyncTaskと直ぐに思ってしまうけれど、常に使える手ではないということだろう。


じゃ、どうするか。


そもそもやりたかった処理は、メッセージを受信したら画像をダウンロードするというもの。その際Activityは起動しない、Notificationのみ表示する。onMessage()はその処理をキックするだけ。

ムム〜。やっぱりThraed回すのが手堅そう。となると、java.uti.concurrent.Executor あたりにヒントがありそう。

2012年8月10日金曜日

GCMの実装でおさえておきたいポイント

前回の記事「Google Cloud Messaging(GCM)とC2DMの主な違いを挙げてみた」を書いていて、覚えておきたいなと思ったポイントがいくつかあったのでメモメモ。


同一メッセージには同一Collapse Keyを指定する

デバイスがオフラインからオンラインへ復帰した時、GCM Serverはストレージされたメッセージをデバイスへ再送する。

たまたま通信状態が悪かったりして、メッセージが届かないと思ったユーザーが、Client Application Serverから同じメッセージを何回もGCM Serverへ送っていたら、同じようなメッセージをいくつもデバイスで受信することになる。

Collapse Keyを同じにしていると、GCM Server側で、全てのメッセージではなく最新のメッセージ1つだけを選んで送る。

Collapse Keyは適当にランダムなどではなく、同一メッセージで統一させた方がいいなあ。


「Device not Registered」エラーが出たら、Client Application ServerからRegistration IDを削除する

デバイス側でGCMを使ったAndroid Applicationが削除された場合、次回メッセージ送信時に「Device not Registered」エラーが出る。

このとき、Client Application Server側で、非有効なRegistration IDをクリーンアップした方がいいなあ。


デバイスのバッテリーを節約するには「delay_while_idle」の指定が効きそう

「delay_while_idle」はC2DM時代からあるパラメータ。デバイスがオンラインでも、unlockされてデバイスの操作が始まらない限りメッセージを送らない、という指定ができる。

メッセージの即時性は無くなるけれど、デバイスが起きているときにだけメッセージが送られるので節電効果があるなあ。

2012年8月9日木曜日

Google Cloud Messaging(GCM)とC2DMの主な違いを挙げてみた

Google I/Oのプレゼン動画から、Google Cloud Messaging(GCM)の特徴やC2DMとの主な違いを挙げてみた。

C2DM時代は・・・
  • 開発者が利用するにはフォームの入力が必要でアクティベーションのメールを受信するという流れ
  • Sender Auth Tokenの取得にClient Loginを使用
  • 一日あたり20万メッセージまでというクオータの制限があった

GCMでは・・・
  • Google APIs Consoleにプロジェクトを追加してGCMを有効にするだけで利用できるようになる
  • 脱Client Login
  • クオータ制限なし

GCMでも変わらないこと
  • 無料のサービス
  • Froyo以上で稼働
  • メッセージ受信にRegistration IDが必要
  • メッセージがデバイスへ届くまでの基本的な流れ(Client Application Server→GCM Server→Android Device)

GCMで変わったこと
  • Project IDが必要
    Sender IDの代わり。Google APIs Consoleでプロジェクトを追加した時に取得。
  • API Keyが必要
    Sender Auth Tokenの代わり。Google APIs Consoleのプロジェクト管理画面で取得。
  • GCM Serverがjson形式のデータにも対応。json形式でリクエストを受け付けたりレスポンスを返したりするようになった。

GCMで追加・改善された機能

1.Message Multicasting
メッセージを複数デバイスへ一斉送信できるようになった。最大1,000台まで。

2.Expiring Messages
メッセージを再送する期間を指定できるようになった。

「time_to_live」パラメータにメッセージが有効な期間を秒単位で設定。0秒から4週間(デフォルト)まで指定可能。0秒の時はGCM Serverにメッセージは保存されずに直ちにデバイスへ送られる。この時デバイスがオフラインだったらメッセージは捨てられる。

指定した値をオーバーした段階でGCM Serverはメッセージをクリーンアップする。

3.Messages with Payload
メッセージにデータを含められるようになった。1メッセージあたり最大4KBまで。主に、チャットなどの簡単なテキスト送信に利用できる。

オフライン時間が長くてメッセージがたくさん溜まってしまっていたら、オンラインへ復帰した時に、GCM Serverは全てのメッセージを削除して「たくさんメッセージが溜まっていますよ」という1メッセージだけをデバイスへ送る。削除されたメッセージ数はGCM Serverからのレスポンスに含まれている。

デバイスがオフライン時にGCM Serverへ保存しておけるのは、最大100メッセージまで。

4.Speed
メッセージ送信がめっちゃ速くなった。GCM Serverへリクエストが届いてからメッセージを送り出すまで4.7ミリ秒。

5.Multiple Senders for Social Updates
1つのAndroid Applicationで複数のプロジェクトのメッセージを受け取れるようになった。自社サービスのメッセージも、Google Plusなどソーシャルサービスなどのメッセージも。ソーシャルサービスのProject IDをどうやって取得するのか、そのフローがよくわからない(´・ω・`)。

最大100個までのProject IDが指定可能。


補足1:GCMの信頼性はいかほど?
Reliable Message Queue(RMQ)の働き

RMQは、GCM Serverとデバイス側のGCM Frameworkとの間で動作するもの。メッセージが確実にデバイスへ届いたのかチェックする。

例えば、GCM Serverがメッセージを送った時、デバイスの接続が切れてしまい、メッセージが届かなかったとしても、接続が復帰してデバイスがGCM Serverへアクセスした際に、メッセージが届いていないことをRMQが探知する。そして、GCM Serverはメッセージを再送する。

ACKを送り合う事で到達の確認をしている。いちいちではなく、ある程度のまとまりでACKを送っている。

このような仕組で信頼性が高いシステムになっているのがGCMの強み。GCMはメッセージを無くさない( ̄^ ̄)


補足2:Android Developer ConsoleでGCM Serverが処理したメッセージの統計が見られるようになった

そのアプリでGCMへ登録された数や、OSバージョンなどの端末情報、メッセージが送られた数やCollapseされた数、GCM Serverからのエラーメッセージなどが見られる。


補足3:ICS以上からGoogle Accountをデバイスへ登録する必要が無くなった

ICS以上から、デバイスへGoogle Accountを事前登録する事無くプッシュサービスが使えるようになったとな。本当かな?こんど検証してみようっと。


おまけ:デバイスからメッセージを送れるようになる?

その機能を含んだAPIはあるけれど開発者向けには公開していない、と。それが出来るようになると、またパラダイムが変わりそう。期待したい。


超おまけ:スピーカーのFrancescoがキュートやわ〜(*^_^*)

Yahoo!の国別メダル獲得ランキングのロジックが気に入らない。Bookmarkletを使ってランキングを変えたった。

Yahoo!のロンドン・オリンピック特集ページにある、国別メダル獲得ランキングのロジックが気に入らない。

まず、
  1. 金メダルでソート
  2. 金メダル数が同じだったら銀メダルでソート
  3. 銀メダル数が同じだったら銅メダル数でソート
というロジック。

つまり、銀メダルや銅メダルを何枚とっても、金メダルが取れなかったら、遥かに低いランクに甘んじなければならない。

確かに、オリンピックで金メダルをとることには世界No.1の重みがある。でも、惜しくも銀メダルや銅メダルだったとしても、ランクにほとんど影響がない程度の価値なのか?

不服だヽ(`Д´)ノ


と思っていたら、同じようなことを考えた方がいたらしく、しかも、Yahoo!のページをソートし直すBookmarkletまで作ってくれていた。

国別メダル数ランキングを俺基準でランク付けし直すJS/Boookmarklet作ったった - UTAGEWORKS

国別メダル獲得ランキングを開き、Bookmarkletとして登録したJSのスクリプトをクリックすれば、自分好みのランキングへ変更できる。


個人的に、もっとシンプルなロジックにしたかったので、金3ポイント、銀2ポイント、銅1ポイントへ変更させてもらった。

Before: 現状のランキング(金ソート→銀ソート→銅ソート)

After1: ランキング変更(金3pt、銀2pt、銅1pt)


メダルがとれただけで素晴らしいじゃないか、みな等しく1ポイントだ、とするとこうなる。

After2: ランキング変更(金1pt、銀1pt、銅1pt)


これで溜飲が下がったよ(^o^)/

2012年8月3日金曜日

Googleアカウントへ接続を許可したアプリを調べる

Googleアカウントへ接続を許可したアプリを調べる方法が、意外に面倒くさいので忘備録としてメモ。

1. 「アカウント設定」をクリック
GoogleのトップページなどGoogle内のサイトへ移動して、画面上のアカウントアイコンをクリックする。続いて、その中の「アカウント設定」をクリックする。


2.「セキュリティ」をクリック
画面左の「セキュリティ」をクリックする。


3.「編集」をクリック
「アプリケーションとサイトを認証する」の右側「編集」をクリックする。


これでようやく、Googleアカウントに許可されたアクセスの一覧が表示される。


アプリ名 ー アクセスが許可されたGoogleのサービス アクセスを取り消すボタン」という順で出力される。

ただし、Androidのアカウントマネージャーでアカウントが管理されている場合は、アプリ名の部分が「Android」とまとめられて、どのアプリで許可したアクセスかがわかりづらくなる・・・。


Twitterでは、連携アプリの一覧の表示の中に、サムネイルや許可した日時、アプリの紹介文などが入っていて視覚的に分かりやすい。


Googleもこのような表示にしてくれればいいのに。

2012年7月31日火曜日

手嶌葵ちゃんコンサートへ@町田 : 2012/07/29 セットリストもアップしてみた

7月29日、町田市民ホールで開かれた「手嶌葵 コンサート~心揺さぶる天使の歌声をあなたに~」へ逝ってきた\(^o^)/

町田市民ホールは800席以上の大きなホール(完売御礼が出ていて良かったねぇ葵ちゃん)。身長が低い私でも前の人の背が邪魔にならないように座席が構成されていて、ステージがとても見やすかった。PAもハウリングせず、葵ちゃんの声が聴きやすかったし、なかなか良いホールだった。

メンバーは、葵ちゃん(vocal)、Yancyさん(piano)、オオニシユウスケさん(guitar)の3人構成。Yancyさんは昨年末のビルボードライブ東京でのコンサートに引き続き。葵ちゃんと彼のピアノの相性は良いような。オオニシユウスケさんはとってもノリノリだった。弾いていない時も、葵ちゃんの歌声に聞き惚れていらっしゃったなぁと。

葵ちゃんのコンサートはできるだけ欠かさず行ってきたが、今回は、シンプルだけれど内容が濃厚で、大満足の約2時間だった。

演奏曲は、アンコールを含めて全部で25曲。頑張ってセットリストを記憶したけれど、間違いがあったらスミマセン。

セットリスト:
  1. Moon River
  2. Beauty And The Beast
  3. Smile
  4. Night And Day
  5. 元気を出して
  6. サリー・ガーデン(アイルランド民謡)
    英語で歌ってくれました。日本語歌詞バージョンは「春の歌集」に「願いごと」というタイトルで収められています。
  7. 空へ
    ライブでたまぁに演奏してくれるレア曲。葵ちゃんの澄んだ歌声とジャジーなアレンジがとても良かったです。
ここからは、葵ちゃんが大好きなオードリー・ヘプバーン主演の映画から3曲続きます。「クイズです。どの映画の曲でしょう。」と言っていたのに、結局歌い終わると「『麗しのサブリナ』からでした〜」と言ってしまっている葵ちゃん。クイズにならないじゃないかぁ^_^; ま、そこが葵ちゃんらしいところ。
  1. La Vie En Rose 「麗しのサブリナ」から
  2. Fascination 「昼下りの情事」から
  3. 'S Wonderful 「パリの恋人」から
    この曲を葵ちゃんが歌ってくれたは初めて聞きました。
  1. Cruella De Vil ディズニー映画「101匹ワンちゃん」から
    初お披露目。ちょっとクールで色気があって、良かったです。
映画「コクリコ坂から」2曲。
  1. 朝ごはんの歌
  2. さよならの夏
葵ちゃんが大好きな「シンデレラ」映画から2曲。
  1. Secret Kingdom
  2. A Dream Is A Wish Your Heart Makes
ここからゲームやCMで歌った曲特集。
  1. 月のぬくもり
    これもコンサートではレア曲ですね。
  2. エレファン
  3. この道
    これがコンサートで演奏されるとは。英語の唄も発音がキレイで聞きやすいのですが、葵ちゃんが歌う日本語の唄は言葉が心にすっと入ってきて素敵です。
  4. 流星
    今回の「流星」はこれまでで一番良かったと思います。手嶌葵色に染まった「流星」になっていました。
葵ちゃんはマリリン・モンローも大好きということで、マリリンの曲を初お披露目。
  1. I Wanna Be Loved By You
    「ププピドゥ」がとてもキュートでした。
  2. Que sera sera
    「ケ・セラ・セラ」も初お披露目。「いつかCDに入るかも。期待していてね。」ということでした。

  3. The Rose
    今回の「The Rose」はいつにも増して良かったです。最後には雪の中から真っ赤なバラが花ひらく絵が見えたほどです。
アンコールでは2曲演奏してくれました。
  1. 瑠璃色の地球
    いじめの事件など悲痛な話が多いけれど、自分ができるのは歌うことだけと言って、この曲を歌ってくれました。まさに「天使の歌声」で、神がかっていたほど素敵でした。
  2. エーデルワイス
    最後は、祖国よ永遠なれという思いを込めて、みんなでエーデルワイスを合唱。若い葵ちゃんから「祖国」という言葉を聞いて嬉しくなりました。

今度は「Angel」や「Calling You」も歌ってほしいな。

それに野外コンサートもまたやって欲しいな。2009年の「クレマチスの丘」キャンドルナイトでのコンサートは、本当に素敵だったもの。星降る夜に葵ちゃんの歌声に包まれると、完全にトリップします。

2012年4月24日火曜日

Windows7(アップグレード版)をVMware Fusionにインストールするときの注意点

VISTAを7へアップグレードしようかと思って買っておいたアップグレード版のWindows7。

でもVISTAをそのままにしておきたくなって、仮想マシンとしてMac上に64bit版のWindos7を入れようとしたら、ライセンス認証で躓いてしまった。エラーが表示されて認証が通らないのだ。

「0xC004F061: ソフトウェア ライセンス サービスで、指定されたプロダクト キーはアップグレードにのみ使用できることが判明しました。クリーン インストールには使用できません」

参考:アクティブ化でエラー 「0xC004F061: ソフトウェア ライセンス - Microsoft Answers


ポイント:Windows7をVMware上にインストールするには2回インストール作業が必要

7のアップグレード版は、XPやVISTAのプロダクトキーを入力してアップグレードとみなされるのではなく、稼働している対象OS上でアップグレード作業を行ってはじめてアップグレードとみなされる


よって、

1. 一度、敢えてプロダクトキーを入力せずに体験版モードでWindows7をインストール
2. そのすぐ後で、そのWindos7上のROMからアップグレード版インストール作業を重ねて行う
3. 二度目のインストールの時だけ、プロダクトキーを入力する

という手順でインストールを進める必要がある。

つまり2回のインストール作業が必要で、プロダクトキーを入力するのは2回目のみだ。

VMware Fusionでは、予めプロダクトキーやユーザーアカウントを設定しておく簡易インストールが可能だが、それも敢えて行わない方がいい。プロダクトキーが要求される際に空欄でインストールを進めていることを自分で確認した方がいい。

この手順で、無事にアクティブ化が完了した)^o^(


その後、延々とモグラたたきのように、Windows Updateをかけ・・・。

最初にインストールした時は、認証エラーが出るとは思わず、散々Windows Updateをかけていたが、結局アクティブ化できずに時間を無駄にしてしまったorz。


ポイント:ライセンス認証が通ってからWindows Upadteをするべし

2012年3月13日火曜日

Notificationをタップしてギャラリーで画像を開く

画像のダウンロードが完了した際にNotificationを表示している。

Notificationをタップして確認のため画像を開くときに、標準のギャラリーで開きたいなと思って書いてみたコード。

protected void onPostExecute(String description) {
 if (error) {

  showNotification(DOWNLOAD_CANCELED,"Data download ended abnormally!", "", null);

 } else {

  //開いてみるだけなのでACTION_VIEWをセット
  Intent intent = new Intent(Intent.ACTION_VIEW);

  intent.setType("image/*");

  //ターゲットの画像URIを設定
  intent.setData(download_uri);

  //PendingIntentを作成
  PendingIntent contentIntent = PendingIntent.getActivity(myAppContext, 0, intent, 0);

  //Notificationを表示
  showNotification(DOWNLOAD_DONE, "Data download is complete!",description, contentIntent);

 }
}

private void showNotification(int mode, String contentTitle,String contentText, PendingIntent contentIntent) {

 Notification notification = null;
 long now = System.currentTimeMillis();
 switch (mode) {
 case DOWNLOADING:
  notification = new Notification(android.R.drawable.stat_sys_download, contentTitle, now);
  notification.defaults |= Notification.DEFAULT_LIGHTS;
  notification.defaults |= Notification.DEFAULT_SOUND;
  notification.flags = Notification.FLAG_ONGOING_EVENT;
  break;
 case DOWNLOAD_DONE:
  notification = new Notification(android.R.drawable.stat_sys_download_done, contentTitle,now);
  notification.flags = Notification.FLAG_AUTO_CANCEL;
  break;
 case DOWNLOAD_CANCELED:
  notification = new Notification(android.R.drawable.ic_menu_close_clear_cancel,contentTitle, now);
  notification.flags = Notification.FLAG_AUTO_CANCEL;
  break;
 }

 notification.setLatestEventInfo(myAppContext, contentTitle,contentText, contentIntent);
 notificationManager.notify(notification_id, notification);
}

Notificationをタップすると・・・

ダウンロード後の画像がギャラリーで開きます。


Notificationとギャラリーを絡めたサンプルがあまり見当たらなかったので忘備録としてメモメモ。

2012年3月9日金曜日

Cursorのclose()が原因で Attempted to access a cursor after it has been closed

Activity.managedQuery()でCursorを取得し、データ取得後にCursorをclose()していたら、close()のタイミングが悪くてRuntimeExceptionが発生。

java.lang.RuntimeException: Unable to resume activity {jp.hogehoge.test/jp.hogehoge.test.DownloadActivity}: android.database.StaleDataException: Attempted to access a cursor after it has been closed.

ソースの抜粋
@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.download);

 // MediaStoreのDBを保存日の降順で検索
 Cursor cursor = managedQuery(URI, PROJECTIONS, WHERE,WHERE_PARAM, MediaStore.Images.Media.DATE_ADDED+ " desc");

 if (cursor.moveToFirst()) {
  do {
  } while (cursor.moveToNext());
 }
 cursor.close();

}// onCreate()

他のActivityを前面に出し、再度、DBからの一覧を表示した元の画面へ戻ってきたタイミングで発生する。

原因は、onCreate()の中でcursor.close()していたこと。

Activityのライフサイクルからいうと、再度前面へ戻ってきた時にonCreate()はコールされないので、DBからの一覧を取得できず「a cursor after it has been closed」となるわけだ。ごもっとも。

close()を明示的にコールしなければExceptionは発生しなくなった。


だがしかし、close()しないのはお作法として良いのか?と思い検索してみたら、Stack Overflowに参考になるポストがあった。
参考:managedQuery() vs context.getContentResolver.query() vs android.provider.something.query() - Stack Overflow

managedQuery() will use ContentResolver's query(). The difference is that with managedQuery() the activity will keep a reference to your Cursor and close it whenever needed (in onDestroy() for instance.) If you do query() yourself, you will have to manage the Cursor as a sensitive resource. If you forget, for instance, to close() it in onDestroy(), you will leak underlying resources (logcat will warn you about it.)

managedQuery() では、Activityがカーソルへの参照を保持していて必要なくなった時(onDestroy()が呼ばれた時)にクローズされる、とある。よって、明示的にclose()しなくてもよさそうだ。

ちなみに、ContentResolver.query()を使った場合は、自分で明示的にクローズしないとリークが起こるよとある。メモメモ。


managedQuery() はdeprecatedなので、本来なら使いたくないのだが、代替となるCursorLoaderはHoneycomb以上でしか使えない。

まだまだGingerbread搭載機種が発売されている現状では自分でコンパチビリティを書かないといけない。面倒なので使っていない\(^o^)/

Activity.managedQueryの絞り込み条件はどう指定するの?

ActivityのmanagedQueryで絞り込み条件はどう指定するの?

と思った時に、あまり絞り込み条件を含んだサンプルが無かったので忘備録としてメモ。

private static final Uri URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
// 検索する列を指定
private static final String[] PROJECTIONS = { MediaStore.Images.Media._ID,MediaStore.Images.Media.DATE_ADDED, MediaStore.Images.Media.SIZE, "width", "height" };

// where句(絞り込み条件)を指定
private static final String WHERE = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + "=?";

// where句の値を設定
private static final String[] WHERE_PARAM = { "hogehoge" };

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.download);

 // MediaStoreのDBを保存日の降順で検索
 Cursor cursor = managedQuery(URI, PROJECTIONS, WHERE,WHERE_PARAM, MediaStore.Images.Media.DATE_ADDED + " desc");

要は、SQLiteの文法に則って記述すれば良かったのね。
参考:SQLiteでデータベース - 愚鈍人

絞り込み条件が複数ある場合は、

xxx = ? and yyy =?

と条件を指定し、記述した順番にString[]へパラメータを列挙する。

String[] WHERE_PARAM = { "xxxのパラメータ", "yyyのパラメータ" };

2012年3月8日木曜日

SQLiteの時刻は秒が基本のUnix Timeで管理されている。1000をかけてミリセカンドへ変換するのがミソ。

AndroidのSQLiteに保存した日時を取得する際、1970年1月X日という表示になってちょっとハマった。

ポイントは、SQLiteの時刻はエポックタイム(1970年1月1日)から「何秒」経過しているかというUnix Timeで管理されているが、java.util.Date.getTime()が返すlong値はエポックタイムから「何ミリ秒」経過しているかを計算しているということだった。

単純に1000をかけることで解決。
こんな感じ。

if (cursor.moveToFirst()) {
  do {
    // 保存日を取得
    long date = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_ADDED));

    // SQLiteは秒ベースのUNIX時間で管理されているので1000を掛けてミリセカンドへ単位変更
    date *= 1000;

    // 年月日時分で保存日をフォーマット
    CharSequence dateClause = DateUtils.formatDateTime(getApplicationContext(), date,DateUtils.FORMAT_SHOW_TIME |  DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR);

    // 保存日をTextViewに設定
    TextView download_tv_date_added = (TextView) row.findViewById(R.id.download_tv_date_added);
    download_tv_date_added.setText(dateClause);
  } while (cursor.moveToNext());
}
cursor.close();

2012年2月16日木曜日

GoogleのC2DMサーバーのセキュリティ例外を無視させる

GoogleのC2DMサーバーはオレオレ証明書を使っているので、Javaのプログラムでメッセージをを送るとき、https経由だとSSLHandshakeExceptionが発生してしまう。

それを回避するプログラム。

private static final String SERVER_URL = "https://android.apis.google.com/c2dm/send";

java.net.URL c2dmUrl = new java.net.URL(SERVER_URL);

javax.net.ssl.HttpsURLConnection conn = (javax.net.ssl.HttpsURLConnection) c2dmUrl.openConnection();
conn.setDoOutput(true);

// C2DMサーバーの自己証明書の例外を無視する
javax.net.ssl.KeyManager[] km = null;
javax.net.ssl.TrustManager[] tm = {
 new javax.net.ssl.X509TrustManager() {
  public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {}
  public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {}
  public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
 }
};
javax.net.ssl.SSLContext sslcontext= javax.net.ssl.SSLContext.getInstance("SSL");
sslcontext.init(km, tm, new java.security.SecureRandom());
conn.setSSLSocketFactory(sslcontext.getSocketFactory());

この処置でもまだ'HTTPS hostname wrong:'エラーが発生する。証明書のホスト名とアクセスしているホスト名も違うようだ。

よって更にホスト名違いも無視するように設定を追加。

conn.setSSLSocketFactory(sslcontext.getSocketFactory());

// 証明書にあるホスト名とアクセスしているホスト名の違いを無視する
conn.setHostnameVerifier(
 new javax.net.ssl.HostnameVerifier() {
  public boolean verify(String host, javax.net.ssl.SSLSession ses) { return true; }
 }
);


もう、どれだけずさんな証明書なんだか ┐('~`;)┌

2012年1月19日木曜日

C2DMを使ったアプリケーションを作るために必要なもの、必要なこと

必要なもの
  • Google C2DM Server
  • Application Server
  • Android Application
  • Client Application

必要なこと

[Google C2DM Server]
登録に必要なもの
  1. 開発者の連絡先メールアドレス
  2. Sender ID(C2DMサーバーへデータを送信する際に利用されるメールアドレス)

    Sender ID毎に利用できるクオータ値(一日あたり20万メッセージまで)が割り振られる。なので、Sender IDはグローバルなアドレスを設定するのではなく、C2DMを使うアプリケーション毎に個別に設定した方がよさそう。
  3. Android Applicationのパッケージ名(e.g. com.example.xxx)
登録後に、Sender Auth Token を取得する

[Application Server]
  • Client Applicationをデプロイする

[Android Application]
  • C2DMを使ったアプリを開発する

    Android OS 2.2(Froyo)以上が対象

    最低1台の端末にはGoogle Accountの設定が必要
アプリのマニフェストには以下の情報を必ず埋め込む
  1. Sender ID
  2. Android Application のパッケージ名(e.g. com.example.xxx)
  3. アプリはGoogle C2DM サーバーからデータを受信するためにBroadcast Receiver を実装して作る
  • アプリからGoogle のC2DMサーバーへリクエストを送りディバイスを登録する

    レスポンスからRegistration ID を取得する

    Registration IDをアプリ内に保存する

[Client Application]
  • Google のC2DMサーバーへリクエストを送るアプリケーションを開発する
そのアプリケーションでは、HTTPS通信で、以下の情報をC2DMサーバーへ送る
  1. Sender Auth Token
  2. Registration ID
  3. データ※1024バイト以下