2012年3月9日金曜日

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; }
 }
);


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