2011年10月26日水曜日

RのID等の値が定数でなくなったためswitch文でエラー(ADTr14)

Non-constant Fields in Case Labels

どこかで聞いてたけど、変換の仕方忘れてたのでメモ。

switchステートメントにカーソル合わせて、WindowsはCtrl+1、MacはCmd+1を押す。
すると、ifステートメントに早変わり!

2011年10月3日月曜日

Android端末のAPN切り替え

Managing APN Settings on Google Android
基本的には上記リンク先でいける。

調べてて気になったことを以下に。
・端末(OSバージョン?)によって、ソース内コメントのテーブル定義より拡張されていることがある。GalaxySは'nwkname'というカラムがあるが、Xperia Arcにはなかった。
・またpreferapnによるAPN変更時は、ContentValuesのkeyを'apn_id'とする必要がある。(値は'_id'カラムのものを使用する)指定した条件が一意のものでないと、APNは切り替わらない。
SharedPrefernceにapn_idという使用中のAPN情報を持っているようだ。


com.android.providers.telephony.TelephonyProviderを読むと、もう少し細かい仕組みがわかった。(早くandroid.git.kernel.org復旧しないかな)

指定できるURIは5つある。
content://telephony/carriers/
=>URL_TELEPHONY
content://telephony/carriers/current
=>URL_CURRENT
content://telephony/carriers/#
=>URL_ID
content://telephony/carriers/restore
=>URL_RESTOREAPN
content://telephony/carriers/preferapn
=>URL_PREFERAPN

それぞれ、SQLクエリによって動作が異なる。
URL_TELEPHONY
=>条件通りのCRUDを行う。

URL_CURRENT
=>query()はcurrent列がNULLでないAPNを取得。insertは特殊で、current IS NOT NULLの行をNULL化してnumericが同一の行のcurrent列を1にする。delete/updateは特徴なし。

URL_ID
=>uriのgetLastPathSegmentなどを条件としてCRUDを行うようだけど、動作よくわからず。このUriに対してLastPath叩くと"carriers"が返ってくるし。便利な使い方でもあるんだろか。

URL_RESTOREAPN
=>delete()でのみ使用可能。preferapnの設定や追加したAPNを初期化する。

URL_PREFERAPN
=>query()は現在設定されているAPNを返す。insert/updateはvaluesで指定したapn_idを設定して、APNを切り替える。deleteはpreferredAPNを初期化する。

以下は、調べる時に使ったソースコード。検証用にログ出すだけのメソッド多め。APNクラスはsetter/getterが無駄に長いので省略。

package com.test.apn;

import java.util.LinkedList;
import java.util.List;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

import com.test.apn.entity.APN;

public class ApnManager {

    private static final String TAG = ApnManager.class.getName();
    
    private static final Uri APN_TABLE_URI = Uri.parse("content://telephony/carriers/");
    private static final Uri APN_PREFERRED_URI = Uri.parse("content://telephony/carriers/preferapn");
    private static final Uri APN_CURRENT_URI = Uri.parse("content://telephony/carriers/current");
    private static final Uri APN_ID_URI = Uri.parse("content://telephony/carriers/#");
    private static final Uri APN_RESTORE_URI = Uri.parse("content://telephony/carriers/restore");
    
    private Context mContext;

    public ApnManager(Context context) {
        mContext = context;
    }

    public List<APN> getApnList() {
        ContentResolver resolver = mContext.getContentResolver();
        Cursor c = resolver.query(APN_TABLE_URI, null, null, null, null);
        printAllData(c);
        
        c.moveToFirst();
        List<APN> apns = new LinkedList<APN>();
        while (! c.isAfterLast()) {
            apns.add(createAPNEntity(c));
            c.moveToNext();
        }
        c.close();
        
        return apns;
    }
    
    public APN getCurrentPreferredApn() {
        ContentResolver resolver = mContext.getContentResolver();
        Cursor c = resolver.query(APN_PREFERRED_URI, null, null, null, null);
        printAllData(c);
        
        if (! c.moveToFirst()) {
            return null;
        }
        
        APN apn = createAPNEntity(c);
        c.close();
        return apn;
    }
    
    public void restoreApn() {
        ContentResolver cr = mContext.getContentResolver();
        int restoreCount = cr.delete(APN_RESTORE_URI, null, null);
        Log.d(TAG, "restore = " + restoreCount);
    }
    
    public APN getApn(long id) {
        ContentResolver resolver = mContext.getContentResolver();
        Cursor c = resolver.query(APN_TABLE_URI, null, "_id="+id, null, null);
        if (! c.moveToFirst()) {
            return null;
        }

        APN apn = createAPNEntity(c);
        c.close();
        
        return apn;
    }
    
    public void setDefaultApn(long id) {
        ContentResolver cr = mContext.getContentResolver();
        ContentValues values = new ContentValues();
        values.put("apn_id", id);
        int updateCount = cr.update(APN_PREFERRED_URI, values, null, null);
        Log.d(TAG, "update = " + updateCount);
    }
    
    public void getCurrentApn() {
        ContentResolver resolver = mContext.getContentResolver();
        Cursor c = resolver.query(APN_CURRENT_URI, null, null, null, null);
        Log.d(TAG, "current => ");
        printAllData(c);
        
        c.moveToFirst();
        List<APN> apns = new LinkedList<APN>();
        while (! c.isAfterLast()) {
            apns.add(createAPNEntity(c));
            c.moveToNext();
        }
        c.close();
        
        return;
    }
    
    public void getApnId() {
        ContentResolver resolver = mContext.getContentResolver();
        Cursor c = resolver.query(APN_ID_URI, null, null, null, null);
        printAllData(c);
        
        c.moveToFirst();
        List<APN> apns = new LinkedList<APN>();
        while (! c.isAfterLast()) {
            apns.add(createAPNEntity(c));
            c.moveToNext();
        }
        c.close();
    }
    
    private APN createAPNEntity(Cursor c) {
        APN apn = new APN();
        
        apn.setId(c.getInt(c.getColumnIndex("_id")));
        apn.setName(c.getString(c.getColumnIndex("name")));
        apn.setNumeric(c.getString(c.getColumnIndex("numeric")));
        apn.setMcc(c.getString(c.getColumnIndex("mcc")));
        apn.setMnc(c.getString(c.getColumnIndex("mnc")));
        apn.setApn(c.getString(c.getColumnIndex("apn")));
        apn.setUser(c.getString(c.getColumnIndex("user")));
        apn.setServer(c.getString(c.getColumnIndex("server")));
        apn.setPassword(c.getString(c.getColumnIndex("password")));
        apn.setProxy(c.getString(c.getColumnIndex("proxy")));
        apn.setPort(c.getString(c.getColumnIndex("port")));
        apn.setMmsproxy(c.getString(c.getColumnIndex("mmsproxy")));
        apn.setMmsport(c.getString(c.getColumnIndex("mmsport")));
        apn.setMmsc(c.getString(c.getColumnIndex("mmsc")));
        apn.setType(c.getString(c.getColumnIndex("type")));
        apn.setCurrent(c.getInt(c.getColumnIndex("current")));
        
        return apn;
    }
    
    private void printAllData(Cursor c) {
        if (! c.moveToFirst()) {
            return;
        }
        
        StringBuilder columnLog = new StringBuilder();
        String[] columns = c.getColumnNames();
        for (String column : columns) {
            columnLog.append(column);
            columnLog.append(",");
        }
        Log.d(TAG, columnLog.toString());
        
        int colSize = c.getColumnCount();
        while (! c.isAfterLast()) {
            StringBuilder values = new StringBuilder();
            for (int i = 0; i < colSize; i++) {
                values.append(c.getString(i));
                values.append(",");
            }
            Log.d(TAG, values.toString());
            
            c.moveToNext();
        }
        
        return;
    }
}

2011年9月28日水曜日

workerで動作するApacheでphp-mysqlが動作しない

http://www.bruteberry.com/archives/2011/04/phpinfomysql.php

redmineインストール済みのCentOSにphpMyAdmin入れようとして、DBに接続できなくて結構ハマッたのでメモ。
とりあえずApacheの設定をデフォルトのpreforkに戻して解決。

2011年9月22日木曜日

In-app Billingの設定をCSVインポート/エクスポートできる機能がデベロッパーコンソールに追加

http://developer.android.com/guide/market/billing/billing_admin.html#billing-bulk-add


In-app Billingの設定をCSVインポート/エクスポートできる機能がデベロッパーコンソールに追加されてた。いつの間にか。
これは地味に嬉しい機能!
IDはコンテンツ管理してるリモートサーバでも識別材料とするから、リモートサーバを一次ソースとしてCSVに吐き出せば、デベコンで設定するときのtypoによる事故防止等につながる。

ID手打ちとかアナログ過ぎてアレだと思ってたので、少しは改善へ向かってる模様。
あとは、Google Checkoutみたいにアプリの詳細なダウンロード数だけを閲覧できるような仕組みがあるとよいのだけれど。デベコンのアカウントだけができることが多すぎる。

2011年7月14日木曜日

Androidマーケットに関する問題を知り、報告するためには

知識の断片 - In-app Billingで(softbankを除く?)キャリア決済ができない

この件では、以下の点がわからないことがこの問題の見通しが悪い理由となったように思う。
Googleへバグ報告する方法
今回Issueにいきなり書いていた。Dev Guideの示すバグ報告先読んでのことかもしれないけど、これってサンプルコードに関するBTSのような気がする。じゃあ正しい報告の仕方ってどうするんだろう。
Googleが抱える既知の問題を知る方法
問題あがってるようだけど、Googleへちゃんとリーチしてるのか?どのような対処を行う予定なのか?これらの状況が私からは見えなかったからADLで質問することになった。

これらの点については、以下のMLでGoogleの方が参考情報として道筋を立ててくれた。これは、Androidマーケットに関わる所だけだけど。
Android Market クライアントアプリのバージョンアップのお知らせ

Googleへ問題や質問を報告するメールフォーム
Contacting Support

既知の問題(Known Issue)の掲載先
Android Market for Developer Known Issues

注意すべきは、どれも言語をEnglishにしないと見えない点。日本語にすると、Known Issueに至っては、
現在のところ、記録されている問題はありません。
なんてのたまう始末。
翻訳済みでないなら、原文ページに飛ばしてほしい。それかMicorsoftのMSDNみたいに機械翻訳かけてその旨記載しておくとか。

問題かどうかの切り分けとして、最初にMLとかStackOverflowを使うのもよいかもしれない。

2011年6月13日月曜日

Themeを設定したDialogがActivityに重なって表示されない

Themeを指定したProgressDialogが、2.3以降の端末(バージョンアップ後のGalaxySとかXperiaArcとか)で表題のような状態になった。
ちょうど別のActivityに遷移してしまったような感じ。
ProgressDialog dlg = new ProgressDialog(this, R.style.ProgressDialog);


一応下記のようにThemeにはparentでTheme.Dialog.Alertを指定していたのに、Dialogたるべき属性を継承してくれてない。

styles.xml



しょうがないので、継承したかったAlertDialogのThemeから設定を引っ張ってきた。

styles.xml


ネタ元はココ

windowContentOverlayはあってもなくても見た目変化ない気がする。念のため。

2011年5月17日火曜日

In-app Billingで(softbankを除く?)キャリア決済ができない

これは、私個人ではまだ解決に至っていない問題。(2011/5/17時点)

アプリ内課金を実装してみて、いざ実機(Xperia Arc SO-01C)で確認してみると、クレジットカード払いは上手くいったが、「NTT Docomo利用料金と一緒に支払い」を選択すると、購入に失敗する。
spモードパスワードを入力するダイアログを閉じた後、何も音沙汰がない。

ログを確認してみると、spモードパスワード入力ダイアログである、
com.android.vending/.billing.CarrierPasswordActivity
を起動するインテントを飛ばした後に、REQUEST_PURCHASEに対してRESULT_USER_CANCELEDが返ってきている。
継続しなければならない課金認証処理中に、作り上予期してないアクティビティが起動することによって中断されてしまっているのだろうか。

ネットで調べてみると、同様の事象で苦戦している人がいた。
In-app Billing(アプリ内課金)+SPモード決済=動作不良?
私個人ではDocomo以外では未検証だったのだけど、これによればauでも同様に発生する問題みたい。softbankはパスワードを要求されないから大丈夫だったともある。


そもそも、Android2.1以前の端末ではアプリ内課金キャリア決済もまともに動かないようで。
せっかく普及してきたAndroidユーザーの多くが対象にならないのなら、しばらくは様子見がよいのかな。

---------------------
追記(2011/7/5)

Android Developer Lab Tokyoにおいて、AndroidマーケットのセッションでIn-app BillingおよびCarrier Billing(キャリア決済)について触れられていたので、もしかしてこの件知らずにこの機能オススメしてるんじゃなかろうかと心配になった。
そこでこの件について尋ねてみたところ、その場にいらっしゃったGoogleのエンジニアの方からも問題解決を加速するよう働きかけてくださりました。
Issue 23: Direct carrier billing doesn't function

これは既知の問題で、数週間以内に Android Market アプリケーションのアップデートに含まれて修正される予定です。­ いましばらくお待ちください。
とのこと。

----------------------
追記(2011/7/14)

問題修正されたAndroidマーケットアプリが近日リリースされるとのこと。
この問題はこれでおしまいかな。

アプリ内課金のキャリア課金に関して

Android Market クライアントアプリのバージョンアップのお知らせ

ADLで質問してからの流れは非常に早いんだけど、実際言わなかったらどうなっていたんだろう、なんて思った。ほんとはバックグラウンドで動いてただけで私が質問したことなんて何の関係もなかったのならいいんだけど…