Yahoo!地図 SDK for Android

SDKっていうからもっと重厚なやつを想像してましたが、ちっちゃいjar一個とpng六個が同梱されたセットで、単に地図をぺろっと表示するだけならすごく短いコードで出来ました。
Yahoo!地図 SDK for iPhone & Androidを公開しました
Yahoo!地図 SDK for Android

  1. 上のリンクからZIPをダウンロードしておいて適当なところに解凍しておく。
  2. (持ってなければ)Yahoo!JapanデベロッパーネットワークでアプリケーションIDを取得する。
  3. 普通にeclipseでandroidプロジェクトを作成する。
  4. ymap.jarをassetsに置いてbuild path通す。
  5. res/drawable-hdpi/に同梱の画像を置く。
  6. jp.co.yahoo.android.maps.MapActivityをextendsしてActivityを作成する。
public class YMapActivity extends MapActivity {
    private static final String Y_APP_ID = "アプリケーションID";
    private MapView mMapView;
    private GeoPoint mGeoPoint;
    private MapController mMapController;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMapView = new MapView(this, Y_APP_ID);
        mGeoPoint = new GeoPoint(35677695, 139771460);
        mMapController = mMapView.getMapController();
        mMapController.setCenter(mGeoPoint);
        mMapController.setZoom(1);
        setContentView(mMapView);
    }
標準

サテライト

地下街

シンプル

路線図

ミッドナイト

もっといろいろ出来るっぽいので試してみたいですね。

Apache Cassandra 0.7.0 インストール・設定・起動・Javaからインサート(備忘録)

インストール

$ mkdir -p ~/apache/cassandra
$ cd ~/apache/cassandra/
$ wget http://www.meisei-u.ac.jp/mirror/apache/dist//cassandra/0.7.0/apache-cassandra-0.7.0-bin.tar.gz
$ tar xvzf apache-cassandra-0.7.0-bin.tar.gz
$ mv apache-cassandra-0.7.0/ 0.7.0

設定

CASSANDRA_HOME
$ vi /home/ktakeda47/.bashrc
export CASSANDRA_HOME=/home/ktakeda47/apache/cassandra/0.7.0
export CASSANDRA_CONF=$CASSANDRA_HOME/conf
export CASSANDRA_MAIN=org.apache.cassandra.thrift.CassandraDaemon
$ source ~/.bashrc
Java1.6が必要
$ java -version
java version "1.6.0_20"
各種ディレクトリの場所指定
$ cd 0.7.0/
$ vi conf/cassandra.yaml
# directories where Cassandra should store data on disk.
#data_file_directories:
#    - /var/lib/cassandra/data
data_file_directories:
    - /home/ktakeda47/apache/cassandra/0.7.0/data
# commit log
#commitlog_directory: /var/lib/cassandra/commitlog
commitlog_directory: /home/ktakeda47/apache/cassandra/0.7.0/commitlog
# saved caches
#saved_caches_directory: /var/lib/cassandra/saved_caches
saved_caches_directory: /home/ktakeda47/apache/cassandra/0.7.0/saved_caches
$ mkdir data
$ mkdir commitlog
$ mkdir saved_caches
log4jログファイルの出力先指定
$ vi conf/log4j-server.properties
#log4j.appender.R.File=/var/log/cassandra/system.log
log4j.appender.R.File=/home/ktakeda47/apache/cassandra/0.7.0/log/system.log
$ mkdir log
JMXのポート変更
$ vi conf/cassandra-env.sh
# Specifies the default port over which Cassandra will be available for
# JMX connections.
#JMX_PORT="8080"
JMX_PORT="9090"

起動

$ ./bin/cassandra -f
cliでアクセス
$ ./bin/cassandra-cli
Welcome to cassandra CLI.
Type 'help;' or '?' for help. Type 'quit;' or 'exit;' to quit.
[default@unknown] 
[default@unknown] connect localhost/9160;
Connected to: "Test Cluster" on localhost/9160
キースペース作成
[default@unknown] create keyspace Sample1;
[default@unknown] use Sample1;
Authenticated to keyspace: Sample1
カラムファミリー作成
[default@Sample1] create column family Standard1; 
[default@Sample1] create column family Super1 with COLUMN_TYPE=Super and COMPARATOR=BytesType and SUBCOMPARATOR=BytesType;
[default@Sample1] create column family utf8Order with COMPARATOR=UTF8Type;
[default@Sample1] create column family time_utf8Order with COLUMN_TYPE=Super and COMPARATOR=LongType and SUBCOMPARATOR=UTF8Type;
データモデル確認
[default@Sample1] show keyspaces;
       WARNING: Could not connect to the JMX on localhost:8080, information won't be shown.
 Keyspace: Sample1:
  Replication Strategy: org.apache.cassandra.locator.SimpleStrategy
    Replication Factor: 1
  Column Families:
    ColumnFamily: Standard1
      Columns sorted by: org.apache.cassandra.db.marshal.BytesType
      Row cache size / save period: 0.0/0
      Key cache size / save period: 200000.0/3600
      Memtable thresholds: 0.4359375/93/60
      GC grace seconds: 864000
      Compaction min/max thresholds: 4/32
      Read repair chance: 1.0
    ColumnFamily: Super1 (Super)
      Columns sorted by: org.apache.cassandra.db.marshal.BytesType/org.apache.cassandra.db.marshal.BytesType
      Row cache size / save period: 0.0/0
      Key cache size / save period: 200000.0/3600
      Memtable thresholds: 0.4359375/93/60
      GC grace seconds: 864000
      Compaction min/max thresholds: 4/32
      Read repair chance: 1.0
    ColumnFamily: time_utf8Order (Super)
      Columns sorted by: org.apache.cassandra.db.marshal.LongType/org.apache.cassandra.db.marshal.UTF8Type
      Row cache size / save period: 0.0/0
      Key cache size / save period: 200000.0/3600
      Memtable thresholds: 0.4359375/93/60
      GC grace seconds: 864000
      Compaction min/max thresholds: 4/32
      Read repair chance: 1.0
    ColumnFamily: utf8Order
      Columns sorted by: org.apache.cassandra.db.marshal.UTF8Type
      Row cache size / save period: 0.0/0
      Key cache size / save period: 200000.0/3600
      Memtable thresholds: 0.4359375/93/60
      GC grace seconds: 864000
      Compaction min/max thresholds: 4/32
      Read repair chance: 1.0
なんかwarn出てますよorz

Javaからアクセス

package sample;

import java.nio.ByteBuffer;

import org.apache.cassandra.thrift.Cassandra.Client;
import org.apache.cassandra.thrift.Column;
import org.apache.cassandra.thrift.ColumnParent;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.InvalidRequestException;
import org.apache.cassandra.thrift.TimedOutException;
import org.apache.cassandra.thrift.UnavailableException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

public class SimpleInsert {

 public SimpleInsert() {
 }

 public static final void main(String[] args) {
  TTransport tTransport = new TFramedTransport(new TSocket("localhost", 9160));
  TProtocol tProtocol = new TBinaryProtocol(tTransport);
  try {
   tTransport.open();
  } catch (TTransportException e) {
   throw new RuntimeException(e);
  }
  
  Client client = new Client(tProtocol);
  try {
   client.set_keyspace("Sample1");
   ByteBuffer key = ByteBufferUtil.bytes("sample1");
   ColumnParent columnParent = new ColumnParent("Standard1")
    .setSuper_column((ByteBuffer) null);
   Column column = new Column(
     ByteBufferUtil.bytes("hoge"),
     ByteBufferUtil.bytes("sample_value"),
     System.currentTimeMillis());
   client.insert(key, columnParent, column, ConsistencyLevel.ONE);
   tTransport.flush();
  } catch (TTransportException e) {
   throw new RuntimeException(e);
  } catch (InvalidRequestException e) {
   throw new RuntimeException(e);
  } catch (UnavailableException e) {
   throw new RuntimeException(e);
  } catch (TimedOutException e) {
   throw new RuntimeException(e);
  } catch (TException e) {
   throw new RuntimeException(e);
  } finally {
   tTransport.close();
  }
 }

}
確認
$ ./bin/cassandra_cli
なんか入ってる?
[default@Sample1] connect localhost/9160;
[default@Sample1] use Sample1;
[default@Sample1] get Standard1['sample1'];
       => (column=686f6765, value=73616d706c655f76616c7565, timestamp=1296460094273)Returned 1 results.
see also: http://gihyo.jp/dev/serial/01/cassandra/0001
see also: http://d.hatena.ne.jp/amanar/20110109/1294570753

too many SQL variables: , while compiling: ・・・

too many SQL variables: , while compiling: DELETE FROM sample_tbl WHERE rowid IN (?,?,?,...

そもそもこんなエラーには滅多にブチ当たらないだろうし、もしブチ当たったならアプリケーションの設計から見直したほうが良いと思います。はい。
最初このエラーをLogCatで見たときはandroidの問題かなと思いましたが、結論はsqlite3の制限でしたというお話。

例えば、以下のメソッドをアプリケーション側から使うとき。
package android.database.sqlite;

public class SQLiteDatabase extends SQLiteClosable {
    public void execSQL(String sql, Object[] bindArgs) throws SQLException {
        // (中略)
    }
}
第一引数sqlに下記のような"?"プレースフォルダが1000個あるSQL文を、
DELETE FROM sample_tbl WHERE rowid IN (?,?,?,...延々1000個...,?);
第二引数に"?"プレースフォルダにバインドしてもらうパラメータを1000個渡して実行します。JDBCのPreparedStatementを使うときのやり方と同じですね。
するとLogCatには以下のようなログが吐かれて、SQLは失敗してしまいます。
ERROR/hoge.fuga.SampleDao(123): too many SQL variables: , while compiling: DELETE FROM sample_tbl WHERE rowid IN (?,?,?,........
androidのソースを辿っていくと、
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
        // (中略)
        DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
    }

package android.database;

public class DatabaseUtils {
    public static void bindObjectToProgram(SQLiteProgram prog, int index,
            Object value) {
        if (value == null) {
            prog.bindNull(index);
        } else if (value instanceof Double || value instanceof Float) {
            prog.bindDouble(index, ((Number)value).doubleValue());
        } else if (value instanceof Number) {
            prog.bindLong(index, ((Number)value).longValue());
        } else if (value instanceof Boolean) {
            Boolean bool = (Boolean)value;
            if (bool) {
                prog.bindLong(index, 1);
            } else {
                prog.bindLong(index, 0);
            }
        } else if (value instanceof byte[]){
            prog.bindBlob(index, (byte[]) value);
        } else {
            prog.bindString(index, value.toString());
        }
    }
}
prog.bindString(int, String)に行ってみます↓
package android.database.sqlite;

public abstract class SQLiteProgram extends SQLiteClosable {
    public void bindString(int index, String value) {
        if (value == null) {
            throw new IllegalArgumentException("the bind value at index " + index + " is null");
        }
        if (!mDatabase.isOpen()) {
            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
        }
        acquireReference();
        try {
            native_bind_string(index, value);
        } finally {
            releaseReference();
        }
    }
}
native_bind_string(int, String)にジャンプ↓
package android.database.sqlite;

public abstract class SQLiteProgram extends SQLiteClosable {
    protected final native void native_bind_string(int index, String value);
}
frameworks/base/core/jni/android_database_SQLiteProgram.cppまで行き、
static void native_bind_string(JNIEnv* env, jobject object,
                               jint index, jstring sqlString)
{
    int err;
    jchar const * sql;
    jsize sqlLen;
    sqlite3_stmt * statement= GET_STATEMENT(env, object);

    sql = env->GetStringChars(sqlString, NULL);
    sqlLen = env->GetStringLength(sqlString);
    err = sqlite3_bind_text16(statement, index, sql, sqlLen * 2, SQLITE_TRANSIENT);
    env->ReleaseStringChars(sqlString, sql);
    if (err != SQLITE_OK) {
        char buf[32];
        sprintf(buf, "handle %p", statement);
        throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
        return;
    }
}
sqlite3_bind_XXX()関数が「too many SQL variables」と言っているようです。

要はsqlite3としては以下のような制限がありますよと。
http://www.sqlite.org/limits.html#max_variable_number
9.Maximum Number Of Host Parameters In A Single SQL Statement
...(中略)...
the maximum value of a host parameter number is SQLITE_MAX_VARIABLE_NUMBER, which defaults to 999.

SQL文のIN句に渡せる値の数とかWHERE句の条件の数の問題ではなく、sqlite3に渡せるバンドパラメータの上限がデフォルト999個までということ。それが証拠にパラメータを渡す方ではなく、SQL文だけを引数に取る以下のメソッドの第一引数に、IN句の条件を文字列として連結して渡すと失敗しません。
package android.database.sqlite;

public class SQLiteDatabase extends SQLiteClosable {
    public void execSQL(String sql) throws SQLException {
        // (中略)
    }
}

パラメータが1000個もあるSQLを投げつけてくるアプリケーションに問題があると思います。

How about you?

public class Male extends Person {
    private boolean ikemen;
    public Male(boolean ikemen) {
        this.ikemen = ikemen;
    }
    public boolean isIkemen() {
        return ikemen;
    }
    /* No setter... */
}
BloggerでのSyntax Highlighter(ソースコードハイライト)の方法を試したかっただけ・・・

Nexus S開封の儀(今更)

  1. 購入までの経緯
    1. BESTBUYで直接発注
      • 12月6日、正式に12月16日からアメリカBESTBUYで販売開始される旨、発表される
      • Nexus One同様、日本には直接配送(販売)してくれないので、Nexus Oneを入手した人の購入方法などを参考に、販売開始前にMyUS.comというサイトで配送先のアメリカの住所を作っておいて、そこから日本に転送してもらうよう準備しておく。(Googleマップで住所検索するとフロリダの港の倉庫街みたいなところ)
      • 12月16日23時頃(日本時間)、BESTBUYで購入手続き完了する。配送先にMyUS.comで取得した住所を入力。支払い方法は自前のクレジットカード。
      • 直後、「Thank you for your order」というタイトルで「Order Confirmation」のメールが届く。この日は興奮覚めやらぬまま床に就く。
      • 12月17日6時頃(日本時間)、起きてびっくり、「Order canceled」というメールが来てた。「because we were unable to verify your information.」の一文のみで良く原因わからず。ツイッターのTL上でも、世界中で同じことを考えている人がいて「住所が被ったから?」とか「クレジットカードがダメ」だとか「いやいやPayPalでもダメだし」とか、同じようにcanceledな人ちらほら。
    2. 購入代行サービスに切り替える
      • 諦めきれず、今度は購入代行サービス、US-BUYER.comに依頼することに。ユーザ登録後、購入対象の商品のURLと価格を入力して購入代行処理開始。
      • 12月18日3時頃(日本時間)、購入代行サービスから「こちらの携帯は大変人気のようで、既に弊社でも発注しており、次の発注が出来るかどうかわかりませんが、来週になって発注してみることは可能です。」という、結局、買えるのか買えないのか良くわからないメールが届く。年内はムリかなと半ば諦めかけるも「待ちます」と返信する。
      • 12月21日6時頃(日本時間)、購入代行サービスから「本日ショップからメールで『バックオーダーの為、入荷に1-2週間かかります』と連絡がありました。」との連絡入る。年内の入手を諦める。
      • 12月22日10時頃(日本時間)、購入代行サービスから「代行商品が到着しましたのでお知らせいたします。」との連絡!速攻で日本への発送手続きを依頼!
      • 12月23日8時頃(日本時間)、発送手続き完了の連絡。一転して年内入手の希望に胸踊る。
    3. 受け取り
      • 12月23日(米国時間)中に米国ヤマト運輸ポートランド支店を出荷。
      • 12月25日(日本時間)成田小口オペレーションセンターに到着。
      • 12月26日(日本時間)輸入通関。
      • 12月26日(日本時間)国際宅急便サービスセンターから国内発送。
      • 12月28日正午(日本時間)受け取り。27日には家に来てたみたいだけど、不在の為、受け取れず。再配達してもらう。
    4. 購入代金総額
      • 端末代金:$529.99(¥43,110)
      • 購入代行サービス手数用:$106.00(¥8,620)
      • 送料:¥3,200
      • 総額:約¥54,930
  2. 開封の儀
受け取り。梱包はちゃんとされている感じ。

開けたらいきなり裏。

素敵。

同梱品全て。

電池を入れてみる。

電源投入直後。

ようこそ画面。

Googleアカウントでログイン中・・・

Froyoからの機能「データ・バックアップ」。チェック入れておく。

すぐにシステムアップデートの通知。

再起動後、システムアップデート中。

大きさはこのぐらい。

HT03aとの比較。おつかれ、HT03a。

端末情報。一応。

とりあえずADW.Launcherをインストール。

思い切ってHT03aのSIMを刺して運用してみる。

※Bloggerの「投稿の編集」画面の「画像挿入」スゲーやり辛い。。。思ったところに挿入されないし、挿入後「説明を追加」とかすると、画像が本文先頭に飛んでったりする。うまくいく方法は、
  • ブログに載せたい画像をpicasaに全部アップ。画像の順番も入れ替えておく。
  • Blogger「投稿の編集」画面で本文書く前に「画像挿入」。
  • 画像に「説明を追加」する。
  • 本文書く。(先に本文書いておいて出力されたHTMLをローカルのテキストエディタとかにコピペしておいて、「画像挿入」後に、もう一度編集画面にペーストするのが良いかも)
アフォか。使い辛い。

android 1.6 以上の端末にインストールされているAndroidマーケットアプリのアップデート

Android Market Supportさんから「A Message from Android Market」ってメールが来た。
* The details page for every app will now utilize and display the app’s “Promotional Graphic” assets at the top. If you haven’t done so already, we recommend uploading “Promotional Graphic” assets for your apps as soon as possible.
「プロモーション画像(省略可)」のところかな?アプリのスクショ以外に「宣伝用画像(省略可)」ってのもあるんだけど、どこに表示されるんだろか?
* Users will be able to filter applications by content rating. If you haven’t added a content rating for your published apps, you should do so immediately at the Publisher site by December 15 -- applications or games without a rating will be treated as “Mature”.
いよいよフィルタリングが可能になる、と。「公開設定のオプション」のところで、「成人」「13歳以上」「8歳以上」「全年齢」の中から選択するヤツ。12月15日までに設定してないと「成人」向けになってしまうので注意。
* We will be reducing the purchase refund window to 15 minutes.
これはなんだろ?有料アプリ絡みかな?よくわからん。。。
http://goo.gl/RfUzT
UIの変更もあるけど、一番大きいのは、アプリ/コンテンツ製作者に不評だった、「24時間以内なら返品可」という条件が、「15分以内」に変更されたことです。約100分の1に短縮されました
* We will be increasing the maximum size for .apk files to 50MB, from the current 25MB.
.apkサイズ50Mまでおkに。ダウンロードに時間がかかるし、ストレージも圧迫するけど、froyo以降SDにアプリインストールも可能だし、ゲームアプリとかだとそんぐらい必要になるのかな。
* Market will support filtering based on screen sizes and densities, as well as on GL texture compression formats. Filtering is based on <compatible-screens> and <uses-gl-texture> elements in an app’s manifest.
おお、これは大きな変更なのでは?AndroidManifest.xmlでcompatible-screensを書くと、アプリ毎に適した端末のスクリーンサイズと解像度を指定できてマーケットでフィルタリングできると。
GLのほうはようわからん。
* If your app includes wallpapers or widgets, Market will automatically add them to new “dynamic” categories, based on their manifest files.
へぇ~。「dynamic」ってカテゴリは日本語で何になるのか?AndroidManifest.xmlに壁紙とAppウィジェット宣言してると自動的にこのカテゴリにフィルタされると。

12月16日にアメリカでGingerbreadのNexus Sが発売開始になるから、そのタイミングも意識してのアップデートなのかな?

Android マーケットのアプリケーションのレーティング

ACTION REQUIRED:
Starting on November 30th, 2010, it will be mandatory that you add a rating level to your apps in the Developer Console at http://market.android.com/publish when you publish or update an app.
 see also:
http://android-developers.blogspot.com/2010/11/content-rating-for-android-market.html
http://market.android.com/support/bin/answer.py?answer=188189
Google のアプリケーション レーティングを使用して、アプリケーションとそのコンテンツが自分に適しているかどうかをユーザーが判断できるようにしてください。レーティングは、ユーザーが作成するコンテンツや広告、アプリケーションがリンクしているコンテンツなど、アプリケーション内のすべてのコンテンツが対象となります。

Androidマーケットにアプリ公開するときってChromeじゃなくてFireFox使ったほうが良いかもね

Chrome(6.0.472.62ぐらい)だとapkファイルのアップロードとかスクリーンショット画像のアップロードがうまく動かない(Javascript?)。まさかと思ってFireFoxでやったらすんなりw

Unable to launch app ... for broadcast Intent { act=android.appwidget.action.APPWIDGET_ENABLED cmp=... }: process is bad

AppWidgetが、タイトルのようなログを吐いて、ActivityManagerにprocess is badとマークされてしまった。タップするとbroadcast intentを飛ばすようなAppWidgetで、異なる複数のImageViewに同じPendingIntentをandroid.widget.RemoteViews#setOnClickPendingIntentで渡していたのが原因?

こうなったが最後、端末再起動しないと起動させてもらえない。

しかたないので、ImageViewにPendingIntentをセットするのではなく、ボタンを一つだけ配置してそいつにPendingIntentをセットしたらActivityManagerに怒られなくなった。

本当にこんな対処法で良いのか?

ActivityManager#restartPackage(String) is deprecated.

android.app.ActivityManager#restartPackage(String packageName) は非推奨メソッド。
このメソッド、今はただ #killBackgroundProcesses(String packageName) をラップしているだけですよ、とJavadocに書いてある。ソース見ると、
@Deprecated
public void restartPackage(String packageName) {
    killBackgroundProcesses(packageName);
}
って、 #restartPackage って名前が気に入らなかっただけ!?(メソッドの振る舞いが名前と一致して無かったってことかな?)そんな理由で、 android.permission.* まで一個増えちゃったのか・・・
android.permission.RESTART_PACKAGES
android.permission.KILL_BACKGROUND_PROCESSES
しかも、 #killBackgroundProcesses(String packageName) は
since: API Level 8
とりあえず、しばらくは #restartPackage(String packageName) で良いや。。。

android.permission.RECEIVE_WAP_PUSH

Android SMS Pushの活用 【受信】
前回のWAP Push送信Activityクラスと今回の受信Receiverでキャッチボールを試みてみました。しかし、残念なことに日本のdocomoネットワークではWAP Push受信はできませんでした。。。サーバー側でWAP SMSのトラフィックをカットしている模様ですね。
確かにセキュリティ面で問題となってきそうな機能ではあります。
 docomo以外はどうなんだろ・・・
see also: http://groups.google.com/group/android-sdk-japan/browse_thread/thread/9768fc6b55345d07

java.lang.OutOfMemoryError: bitmap size exceeds VM budget

android端末(HT03A)のカメラで撮った画像をそのままBitmapに変換するとOutOfMemoryErrorで落ちる。
public class CameraView extends SurfaceView implements SurfaceHolder.Callback, PictureCallback {
    public void onPictureTaken(byte[] datas, Camera camera) {
        Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length);
        // OutOfMemoryError
ログを見るとこんなことを言われてしまってた。
 java.lang.OutOfMemoryError: bitmap size exceeds VM budget
対処方法はこれ。
BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length, options);
see also:
http://developer.android.com/intl/ja/reference/android/graphics/BitmapFactory.Options.html
http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue