Facebook Android SDKで認証してPostする際のポイント

DroidNPをFacebookに対応させました。
その際のハマりポイントをメモしておきます。
要は、Facebook Android SDKで認証してPostする際のポイントです。
かなり雑な内容ですが。。。



■Facebook Android SDKのDLとimport
https://github.com/facebook/facebook-android-sdk
からDL。
"facebook"ディレクトリをeclipseでimport。
自分のプロジェクトを右クリック。
properties > Android > Library > Add...
にてimportしたプロジェクト(デフォルトだと"com_facebook_android")を選択。OKを押す。



■認証で使用したメソッド
--
com.facebook.android.Facebook.authorize(
	Activity activity,
	String[] permissions,
	int activityCode,
	DialogListener listener)
--

* activity
Contextをキャストしても良いっぽい。
(XxxActivity)getContext()みたいな感じ。

* permissions
"offline_access"と"publish_stream"を指定。
offline_accessは認証tokenの期限切れを防ぐ為。
publish_streamはウォールへの書き込み権限。

* activityCode
startActivityForResultに渡すrequestCodeのようなもの。後述。

* listener
後述。



■認証後の処理
(A) Facebook公式アプリが端末にインストールされている場合
onActivityResultに戻るので、前述のactivityCode == requestCodeの時に専用の処理を書く。

--Java(onActivityResultにて)--
switch (requestCode) {
case REQUEST_CODE_FACEBOOK_OAUTH:
	String token = data.getExtras().getString(Facebook.TOKEN);
	// TODO: tokenの保存等。
	break;
default:
	break;

}
--


(B) Facebook公式アプリが端末にインストールされていない場合

--Java(Facebook.DialogListener)--
@Override
public void onFacebookError(FacebookError e) {
	// TODO:
}

@Override
public void onError(DialogError e) {
	// TODO:
}

@Override
public void onComplete(Bundle values) {
	String token = values.getString(Facebook.TOKEN);
	// TODO: tokenの保存等。
}

@Override
public void onCancel() {
	// TODO:
}
--



■認証サンプルソース
--Java--
Facebook facebook = new Facebook("[Facebookで登録したアプリのID]");
facebook.authorize(
		(XxxActivity)getContext(),
		new String[] {"offline_access","publish_stream"},
		1,
		new Facebook.DialogListener() {
	@Override
	public void onFacebookError(FacebookError e) {
		// TODO:
	}

	@Override
	public void onError(DialogError e) {
		// TODO:
	}

	@Override
	public void onComplete(Bundle values) {
		String token = values.getString(Facebook.TOKEN);
		// TODO: tokenの保存等。onActivityResultも忘れずに。(この場合、requestCodeは1。)
	}

	@Override
	public void onCancel() {
		// TODO:
	}
});
--



■認証後のサンプル
--Java--
private void setFacebookSettings(String token){
	Facebook facebook = new Facebook("[Facebookで登録したアプリのID]");
	facebook.setAccessToken(token);

	try {
		String response = facebook.request("me");
		JSONObject json;
		try {
			json = new JSONObject(response);
			String name = json.get("name").toString();
			String id = json.get("id").toString();

			// TODO: nameやidをSharedPreferencesに保存する。等。
		} catch (JSONException e) {
			// TODO:
		}
	} catch (MalformedURLException e) {
		// TODO:
	} catch (IOException e) {
		// TODO:
	}
}
--



■その他
Facebook側の話。
Android Marketでの公開を前提としている場合、開発用のkey hash登録と併せて
Android Market用のAPK作成時に使用するkeystoreでのkey hash作成/登録も忘れずに。

エミュレータにマーケットアプリ等を入れる。

エミュにマーケットアプリ等を入れたかったので調べました。
先に、参考にさせて頂いたURLです。

参考1:
Android SDKを使おう
http://androidsdk.web.fc2.com/

参考2:(必要なファイルはここからDLさせて頂きました。)
WindowsでAndroid SDKを使おう
http://androidsdk.zouri.jp/

以下、上記”参考1”の手順の自分メモです。
一通り済ませた後、マーケットアプリを起動してGoogleのアカウントを求められるところまで確認しました。

(1)
eclipse等からエミュを作る。

(2)
(Win XPだと) Documents and Settings/[ユーザ名]/.android/avd/[作ったエミュ名].avd/
に、エミュと同一OSバージョンの
android-sdk-windows/platforms/android-[OSバージョン値]/images/system.img
をコピー。

(3)
コマンドプロンプトから
android-sdk-windows/tools
に移動。
emulator -avd A22 -partition-size 100
と打つ。

(4)
コマンドプロンプトをもう一つ起動。
android-sdk-windows/platform-tools
に移動。

(5)
adb -s emulator-5554 shell
エミュレータのルート・シェル(Linuxのコマンドプロンプト)を起動。

mount -o remount,rw -t yaffs2 /dev/block/mtdblock0 /system
システムフォルダを読み取り専用から読み書き可能に属性を変更。

chmod 777 /system/app
システム・アプリのフォルダに書き込みを許可。

exit
エミュレータのルート・シェル(Linuxのコマンドプロンプト)を終了。

adb -s emulator-5554 push Vending.apk /system/app/
マーケット本体のインストール。

adb -s emulator-5554 push GoogleServicesFramework.apk /system/app/
必要なファイルをインストール。

adb -s emulator-5554 push MarketUpdater.apk /system/app/
必要なファイルをインストール。

adb -s emulator-5554 push Development.apk /system/app/
必要なファイルをインストール。

adb shell rm /system/app/SdkSetup.apk
不要なファイルを削除。

* Vending.apk
* GoogleServicesFramework.apk
* MarketUpdater.apk
* Development.apk
は android-sdk-windows/platform-tools に有ることとする。


(6)
2つのコマンドプロンプトを閉じて、エミュレータをすべて止める。
(Win XPだと) Documents and Settings/[ユーザ名]/.android/avd/[作ったエミュ名].avd/

userdata-qemu.img

cache.img
を削除。

(7)
eclipseからエミュレータを起動。

備考:
一通りの手順を終えた段階で
Documents and Settings/[ユーザ名]/.android/avd/[作ったエミュ名].avd/system.img
を別の場所にコピー、保存しておけば再構築が楽??

Android開発環境構築メモ 2011/07/09

Androidの開発環境をeclipseからMOTODEV Studio for Androidに移行しようと思います。

これまでAndroid開発は、Android開発に特化したeclipseで行っていました。
それはつまり、MOTODEVのコンセプトに似ているので、移行しようかなと。
(DBそのまま見れたりしないかな。。。)

いつもの通り、ミニマム構成をメモ。

■Android SDK
http://developer.android.com/sdk/index.html

■MOTODEV Studio for Android 2.2.1
http://developer.motorola.com/docstools/motodevstudio/

■plugin
▼archive
* JStyle 3.6.2
http://mergedoc.sourceforge.jp/index.html#jstyle.html

* subclipse 1.6.18
http://subclipse.tigris.org/servlets/ProjectDocumentList?folderID=2240

▼update site
* FindBugs
http://findbugs.cs.umd.edu/eclipse

android開発環境構築メモ 2011/07/02

eclipseを新しくしたのでメモ。
極力、ミニマム構成。



■Android SDK
http://developer.android.com/sdk/index.html

■eclipse
http://www.eclipse.org

■eclipse plugin
▼archive
* JStyle
http://mergedoc.sourceforge.jp/index.html#/pleiades.html

▼update site
* Android SDK用プラグイン
http://dl-ssl.google.com/android/eclipse

▼Eclipse Marketplace
* DBViewer
* Egit
* FindBugs
* subclipse

Androidで各種プレイヤーの再生情報を取得する方法 2

基本的には、以下の方法となります。
* Androidで各種プレイヤーの再生情報を取得する方法
http://mamor-blog.tumblr.com/post/2525335625/android

今回は、Scrobble Droidについて書きます。
* Scrobble Droid - DeveloperAPI
http://code.google.com/p/scrobbledroid/wiki/DeveloperAPI

Scrobble Droidと連携するプレイヤは、以下のActionのIntentをsendBroadcastしています。
"net.jjc1138.android.scrobbler.action.MUSIC_STATUS"
このIntentをBroadcastReceiverで引っ掛ける事が第一です。

この時、前述の方法と同様に
String artist = bundle.getString("artist");
String album = bundle.getString("album");
String track = bundle.getString("track");
で情報を取得出来るケースがあれば、出来ないケースもあります。
出来ない場合、次の方法を試してみてください。

--【Java】--
long id = bundle.getInt("id");
if(id<1) id = bundle.getLong("id");

if(1<=id) {
    Cursor c = context.getContentResolver().query(
            ContentUris.withAppendedId(
                    MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id),
            new String[]{
                MediaStore.Audio.Media.ARTIST,
                MediaStore.Audio.Media.ALBUM,
                MediaStore.Audio.Media.TITLE
            },
            null,
            null,
            null
    );
    if(c.moveToFirst()) {
        String artist =
            c.getString(c.getColumnIndex(MediaStore.Audio.Media.ARTIST));

        String album =
            c.getString(c.getColumnIndex(MediaStore.Audio.Media.ALBUM));

        String track =
            c.getString(c.getColumnIndex(MediaStore.Audio.Media.TITLE));

        // TODO:
    }
    c.close();
}
--

if(1<=id)としている理由ですが、MixZingでこの方法を試した時、
idが0のIntentが必ず(?)2度発生し、その後に、再生している曲のidが発生した為です。

MediaStore.Audio.MediaのDB構成を熟知しているワケではありませんが、
idが0のレコードは、仮に存在していたとしても、特殊なレコードのはずです。

Android + Velocityで、端末の言語設定に対応したローカルWEBを作る。

assetsに静的htmlを置く方法では、端末の言語設定に対応したローカルWEBを作成できません。
そこで、Android + Velocityを試してみました。
ソースは以下に置いてあります。
http://code.google.com/p/madroom-project/source/browse/#svn%2Ftrunk%2FAndroidVelocity

もしかしたら、そのうちプロジェクト名を変更するかもしれないので、上記URLが無効な場合は
http://code.google.com/p/madroom-project/source/browse/
の中を探してみてください。



以下、ポイントです。
上記URLのソースと照らし合わせてみてください。
(ちょこちょこと手直しするかもしれませんが、ポイントはそれほど変わらないと考えています。)


▼Velocityのログ出力をオフにする
Velocity.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS,
        "org.apache.velocity.runtime.log.NullLogSystem");


▼html側からAndroidのメソッドをcallする
* Velocityを使用する以上、画面遷移毎にプログラムを通す必要があります。
--【java】--
mWebView.addJavascriptInterface(new JavaScriptCallback(), "android");
--

--【JavaScript】--
function transition(page){
    android.transition(page);
}
--

--【java】--
public class JavaScriptCallback {
    public void transition(final String page) {
        mPage = page;
        mHandler.post(new Runnable() {
            public void run() {
                mWebView.loadData(getDataByVelocity(), MIME_TYPE_TEXT_HTML, ENCODE);
            }
        });
    }
}
--


JavaScriptのandroid.transition(page)はJavaScriptCallbackクラスの
transition(String page)を呼び出します。
このpage値により、Velocityに当てはめる値を制御しています。
この時、strings.xmlの値を渡せば、端末の言語設定に対応可能となります。

尚、WebViewの更新は別スレッドで行わないとエラーが出ました。

P.S.
例えば、HELPやチュートリアルの作成に使えるかもしれません。
パラメータの送信については問題が残る。。。

Androidの公式Gmailアプリが新着メール受信したタイミングをキャッチする。

BroadcastReceiverで可能です。
但し、公式Gmailアプリのバージョン(OSバージョン??)によって若干異なります。
Android2.1系とAndroid2.2系以上とで以下のよう2つのBroadcastReceiverを用意する必要がありました。
(記述方法次第では1つにまとめられるかもしれませんが。)
尚、以下のBroadcastReceiverはInboxへの新着のみをキャッチします。

<!-- act=android.intent.action.PROVIDER_CHANGED dat=content://gmail-ls/unread/^i typ=gmail-ls -->
<receiver android:name=".ProviderChangedReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PROVIDER_CHANGED" />
        <data
            android:scheme="content"
            android:host="gmail-ls"
            android:pathPrefix="/unread/^i"
            android:mimeType="*/*"
            />
    </intent-filter>
</receiver>
<!-- act=android.intent.action.PROVIDER_CHANGED dat=content://gmail-ls/unread/^i -->
<receiver android:name=".ProviderChangedReceiver2">
    <intent-filter>
        <action android:name="android.intent.action.PROVIDER_CHANGED" />
        <data
            android:scheme="content"
            android:host="gmail-ls"
            android:pathPrefix="/unread/^i"
            />
    </intent-filter>
</receiver>

P.S.
公式Gmailアプリのデータ取得などは以下の方法で、今のところ可能です。
http://mamor-blog.tumblr.com/post/5437791221/android-gmail

透過率を含めてActivityの背景色を動的に変更する。

Activityの背景色を、透過率を含めて(argbで)変更する場合、以下の形で可能です。

int a = 0;
int r = 0;
int g = 0;
int b = 0;
PaintDrawable paintDrawable = new PaintDrawable(Color.argb(a,r,g,b));
getWindow().setBackgroundDrawable(paintDrawable);

透過率を表すaは0で完全に透明。255で完全に不透明。となります。
なので、"不透明度"と表現して良いと思います。

尚、"android:windowIsTranslucent=true"等、
透過Activityを使用する為の下準備は、当然、必要となります。

Androidの有料アプリ/無料アプリのソース管理。

有料アプリと無料アプリとで、どのようにしてソースの一元管理をしようか。
といった話です。


まず、以下を参考に、Androidライブラリを試してみました。
http://d.hatena.ne.jp/tomorrowkey/20101020/1287584710
http://rokuta96.blog137.fc2.com/blog-category-5.html

つまり、以下のように3つのプロジェクトを作成します。
処理の分岐はgetPackageName()で行うことを前提にしています。

xxxCore
…ライブラリプロジェクト(すなわち実態)
xxxPay
…有料アプリプロジェクト
xxxFree
…無料アプリプロジェクト


しかし、この方法だと、xxxPayとxxxFreeのmanifest.xmlの
<provider>のandroid:authoritiesが重複してしまいました。

結果、後にインストールしようとした方で
INSTALL_FAILED_CONFLICTING_PROVIDER
のエラーが発生してしまいます。

有料/無料アプリの各マニフェストでandroid:authoritiesの値を変えようにも、
そもそも、その実態はライブラリの中。となると、変えることが出来ません。
(Content Providerを継承したクラスの中でAUTHORITYを定義していたので。)

ですので、考えを改め、より原始的な方法(?)でトライしてみようと思います。
具体的には、以下のパッケージ構成。

xxxPay
net.madroom.xxx.core
…実態
net.madroom.xxx.env
…実態

xxxFree
net.madroom.xxx.core
…xxxPayのnet.madroom.xxx.coreへリンク
net.madroom.xxx.env
…実態

net.madroom.xxx.envの中に環境設定クラスを作成。
このクラスに、Content Providerを継承したクラスの中で定義していたAUTHORITYを記述。
当然、Content Providerを継承したクラスの中にあったAUTHORITYは削除します。

これでおそらく、パッケージ構成は完全に同一なまま、
静的なフィールドの値分けが出来ると思います。
getPackageName()による処理分岐も可能なはず。

あるいは、環境設定クラスに
IS_PAY_VERSION = true/false
といったフラグフィールドを用意してもOKと思います。

帰ったら試してみよう。
コケたら追記します。


P.S.
結局は、環境依存する部分をライブラリに含めるな。
という、当たり前の話だったのかもしれません。



2011/05/16 追記

コケました。とは言っても、R.javaが見つからないよ。エラーです。
ですので、xxxFreeで使用するR.javaも、リンクでxxxPayのR.javaを指すようにして解決。
これで、DBもバラバラでうまく動きました。

ホントはアプリ名をそれぞれで少し変えたかったので
valuesのstring.xmlもローカルで別管理にしたかったのですが。
上記の方法だと、それは無理っぽいです。
なので、string.xmlにapp_nameを有料版用と無料版用でそれぞれ用意。
(app_name_payとapp_name_freeみたいな感じです。)
これを、各AndroidManifest.xmlで使い分ける事にします。
(結果的にですが、この方がはるかに楽ですね。)

これで、有料/無料アプリ毎に編集する必要があるファイルは
* AndroidManifest.xml
* 環境設定クラス
の2ファイルのみとなりました。

とりあえず、めでたし。

Android公式Gmailアプリのデータを取得する。

必要なものは
http://grepcode.com/search/?query=gmail
にある
android.provider.Gmail
です。

伴い、以下も必要になります。
com.google.android.collect.Lists
com.google.android.collect.Maps
com.google.android.collect.Sets
上記URLのandroid.provider.Gmailのソースからリンクで辿れます。

モノが揃ったらjarにでもしておきましょう。

細かい使い方は、ソースを見ましょう。ということになりますが。
最も厄介なのは、Android公式Gmailアプリのソースがそもそも非公開である事だと思いました。

About Me

【description】

mamorのブログです。
自分の記事を書きます。
フォローはこのブログからはできません。

【internal link】

【external link】

【search by Google】

【Twitter】

【tag】