kinjouj.github.io

TabLayoutでaddTabした直後にselectしてもスクロールされない件

2015-08-23T04:27:36+09:00 Java Android

Android Design Support Libraryで?追加されているTabLayoutを使った場合に、addTabをした直後にselectメソッドを呼んだりaddTabの第2引数でtrue(selected)にしてもそのタブにフォーカスは当たるけど自動アニメーションスクロールによる処理が行われない為に自分でそのタブが出てる所までスライドしないといけない問題が出てるのですが...

なんとか解決出来たっぽいのでメモ

twitter4j+mockwebserver

2015-08-17T13:54:43+09:00 Java Android

例えばtwitter4jを使う所をテストしたい場合にやたらとAPIリクエスト投げられても困るなっていう事案が発生した場合に、twitter4jが投げるリクエストをローカルサーバーとかにぶん投げるようにしてテストしたいみたいなケースの場合の検証をしてみた

※環境はAndroid(Robolectric)

RxAndroidをざっくり使ってみた

2015-06-11T16:49:40+09:00 Java Android RxJava

参考1: 【翻訳】AsyncTask と AsyncTaskLoader を rx.Observable に置き換える - RxJava Android Patterns

参考2: http://stablekernel.com/blog/replace-asynctask-asynctaskloader-rx-observable-rxjava-android-patterns (参考1の元)

参考3: 俺が RxAndroid について知っているいくつかのまとめ

大分前から色々話題になってるRxAndroidをちょっとざっくりと使ってみた

※あくまで個人的なメモなので詳しく知りたいなら上記の参考を読んだ方が良い

※0.24以降になるとAndroidScheduler以外は無くなるそうなので以下のAppObservableを使うには0.24より上のバージョンでは使えなくなるらしい

Google AppEngine(+OAuth2)にAndroid AccountManagerを介してリクエストする

2015-06-01T16:58:53+09:00 Java Android Google App Engine

chrome.identity APIを使ってGoogle App Engine OAuth2を使うの続き

前回ではJavaScript(Chrome Extension)側からchrome.identityのAPIを利用してアクセスしてレスポンスを取るみたいな事しましたが、AndroidでAccountManagerを使って端末に紐付けしてあるGoogleアカウントを使ってリクエストする場合どうなるのって事でやってみた

gradle-android-test-plugin+robolectric+espresso

2014-04-28T00:00:00+00:00 Android Java robolectric

https://groups.google.com/forum/#!topic/robolectric/xsOpEwtdTi4 にあるように元はJake Wharton氏が開発されていてdeprecatedになっていたgradle-android-test-pluginをrobolectric開発チームによってメンテされるようになった模様

てな訳でgradle-android-test-pluginを使いつつの、robolectricでの単体テスト及びespressoによるUIテストをやってみる

SwipeRefreshLayout

2014-03-30T00:00:00+00:00 Android Java

参考: [Android] Support Library に追加された SwipeRefreshLayout - @adakoda

上記参考によるとAndorid Support v4 packageにSwipeRefreshLayoutっていうのが追加された模様。んまぁ見る限りだとAndroid PullToRefreshとActionBar PullToRefreshを組み合わせたのっぽいような感じもするんですが

という事で使ってみた。ちなみに/path/to/android_sdk/extras/android/support/samples/Support4Demosにサンプルが入ってる

LoadMoreListView

2014-02-26T00:00:00+00:00 Android Java

まぁ要はListViewの末尾に達したら新しいデータをロードする等の仕組みを提供する機能的なやつ。Android PullToRefreshにはそういう機能があったけど、何回かふれているけどActionBar PullToRefreshを使うべきという形になっているので、それっぽい機能をどうするかって所なんですが

調べた所によると、LoadMoreListViewっていうのを利用する事で出来るっぽい。という事で使ってみた

gradle-android-toolkit+robolectric-plugin

2014-02-18T00:00:00+00:00 Android Java robolectric

っていう事でrobolectric-pluginっていうのがあるっては知ってはいたんですが、どうも上手く行かなずな感じで。まぁ一回は挫折したんですが再チャレンジでようやくテストの実行が出来たのでメモっておく

gradle-android-toolkitのallprojects

2014-02-13T00:00:00+00:00 Android gradle

例えばapp/build.gradleがあって

apply plugin: "android"

repositories {
    mavenCentral()
}

dependencies {
    compile "com.actionbarsherlock:actionbarsherlock:4.4.0@aar"
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.1"
}

じゃなくて

apply plugin: "android"

dependencies {
    compile "com.actionbarsherlock:actionbarsherlock:4.4.0@aar"
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.1"
}

的な感じである程度共通化出来る部分は省略したい

ActionBar-PullToRefreshをメニューボタン等で連動させる

2014-02-13T00:00:00+00:00 Android Java

っていうActionBar-PullToRefreshを使っている場合において、メニューでリフレッシュボタンとかでActionBar-PullToRefreshを発生させるような要件を実現したい場合にはどうすれば良いのか。簡単に言えば上記画像中の右上にあるリフレッシュボタンをクリックする事でonRefreshStartedメソッドを発生させたいっていう所

っていうのをやってみた

ActionBar-PullToRefresh

2014-02-12T00:00:00+00:00 Android Java

※ActionBar-PullToRefreshも「PLEASE NOTE, THIS PROJECT IS NO LONGER BEING MAINTAINED」になってますので現時点で使う場合注意

Android PullToRefreshにも追記しましたが、Android-PullToRefresh は既にdeprecated(正確にはNO LONGER BEING MAINTAINED)らしいので、ActionBar-PullToRefreshを使えとのこと。つーことでListFragmentを使う場合のドキュメントを見つつやってみた

※但し、Android PullToRefreshとActionBar PullToRefreshはスタンスは異なるかと思われますので

gradle-android-toolkitでライブラリプロジェクト

2014-02-11T00:00:00+00:00 Android gradle Java

例えば、アプリプロジェクト・ライブラリプロジェクトの両方を持ちつつActionBarSherlock等のような外部のライブラリプロジェクトを利用する場合のgradle-android-toolkitでのビルドってどうなってるかとか辺りをまだやってなかったのでやってみた (※Android Studioに関してはノータッチで)

※以下のバージョンのgradle-android-toolkitはGradleのバージョンが1.10くらい辺りからじゃないと動かないので(ry

Android4.4(Kitkat)のデフォルトSMSアプリに関して

2014-01-29T00:00:00+00:00 Android Java

参考1: Getting Your SMS Apps Ready for KitKat

参考2: Android KITKAT: デフォルトSMSアプリを作る - ブライテクノBlog

※非常に内容がざっくりとしてますので上記参考を(ry

Android 4.4(Kitkat)からデフォルトなSMSのアプリを作って利用する事が出来るらしいとの事は知っていたのだけど検証的な辺りとかしてなかったので色々やってみた

ottoとActivityRecognition

2014-01-27T00:00:00+00:00 Android Java

ottoな勉強をするにあたってそういう要件が必要にならないと例にならない気がしたので、ActivityRecognitionを使って行動認識をActivityへottoを使って通知する仕組み的な事やってみた

android-maven-plugin

2014-01-25T00:00:00+00:00 Android

今更かって感じだけど、android-maven-pluginを使ってAndroidアプリプロジェクトを管理出来るようにしてみる

AndroidSlidingUpPanel

2014-01-21T00:00:00+00:00 Android Java

Android Library Rankingで1位になっているAndroid SlidingUpPanelっていうのが気になったので使ってみた。んまぁAuthor見れば分かるがUmanoっていうアプリで使われてるそうで

でどういうライブラリなのかざっくり画像で見せると

っていう感じになってて下側にあるsliding contentsっていう部分をひっぱることが出来る

まぁ今時は使われてないけど、似たようなのにAndroid公式でサポートされているSlidingDrawerっていうのがある。まぁようはそれっぽい動作をする物みたいな感じ

DialogFragmentのsetShowsDialog

2013-11-25T00:00:00+00:00 Android Java

※ソースを完全に読んだ訳じゃないので若干微妙なのであくまでネタとして

例えば、DialogFragmentを継承したクラスを作りsetArgumentsでandroid.os.Bundleを指定してパラメーターを渡す。そこまでは良いのだけど、そのパラメーターに値が入ってないとかgetSerializableなりはできるけどnullが返ってきてるような場合でそのままonCreateDialogを実行されてしまうとNPEなりの問題が発生してくる訳。そこら辺の制御とか辺りどうするのかって所なんですが

jsonpullparserのcustom_rules.xml

2013-11-23T00:00:00+00:00 Android

Android+JsonPullParserでも書きましたけど、公式にあるcustom_rules.xmlを使うとアノテーションプロセッサのクラスパスに他のライブラリ参照が入ってないので、APT処理によるソースの生成は成功するけどエラーが色々出るのはイケてないなーって思ってて、どうにか修正せねばって事でやってみた

Android+JsonPullParser

2013-11-14T12:00:00+00:00 Android Java

AndroidでJsonPullParserを使うっていうネタ。まぁ公式にもサンプルあるしそれ参考にすれば良いんじゃねって所なんで

※@JsonModel等は前回のをそのまま利用

InputMethodServiceを使ってIMを作るメモ

2013-11-06T00:00:00+00:00 Android Java Python

ネタ帳からの大体そのままな引用(要は雑って事)。以前、Androidのソフトウェアキーボードを作って、PC側とbluetoothを経由してスマフォ上のテキスト入力をPCから入力させるとかっていうのをやってた。とりあえず書いておこうかと

SpeechRecognizerを使って音声テキスト及び音声データを取得する方法

2013-11-05T00:00:00+00:00 Android Java

昔書いたネタをそのまま書く。

※リソース開放方式まで考慮してないので、そこら辺は活用する場合にはそれなりに要修正必須

※onBufferReceivedが機種依存により呼ばれないのもあるそうです

android.speech.SpeechRecognizerを使って音声からテキストを起こすAPIがあるけど、この際に音声データ自体も保管しておくっていう事をする。

SherlockNavigationDrawerを使ってみた

2013-10-23T00:00:00+00:00 Android Java

んまぁSlidingMenuだったりだとか色々あるのですけど、Googleが公開したNavigationDrawer方式をベースに使えるようにした物がSherlockNavigationDrawerらしい、正しいかは微妙だけど。ちなみにデモアプリもあるのでちょっとどんな感じかってのを見たければそれ入れて確認してみるってのも可能

という事で使ってみた (ただ公式なサンプルを見て自分で書いてやってみただけなので、ほとんど同じ)

gradle-android-toolkit+Android Studio (1)

2013-10-19T00:00:00+00:00 Android gradle

普通ならAndroid Studioから「New Project」 -> 「New Module」でAndroidプロジェクトなりモジュールなりどんどん作成していくだけで良いと思うんだけれども、Android公式がサポートしているandroid-toolkitなgradle supportを使ってプロジェクト構成を作った場合なケースに関して検証してみた

Robolectric+powermockito

2013-10-11T00:00:00+00:00 Android Java robolectric

参考: http://addie9000.blogspot.jp/2012/12/robolectricpowermockandroidunit-test.html

Robolectricを使ってJUnit4ベースでテストしたいけど、PowerMockitoを使ってstaticメソッドをテストしたいって思ってもRobolectric自体が@RunWith(RobolectricTestRunner.class)を使うので、PowerMockitoのPowerMockRunner.class指定出来ないよねっていう事で上記の参考にRobolectricでのPowerMockitoの動かし方参考にしつつやってみた。ただ気になる事の検証的な目的かなと

※但し、上記の参考のRobolectricバージョン(1.1)とこの記事のバージョン(2.2)は違う

Android NativeMethods Pattern

2013-10-10T00:00:00+00:00 Android C++ Java

Androidっていうか単純にJNIに関する事だと思うので(ry

おそらくは通常だと

JNIEXPORT JNICALL jstring Java_パッケージ_say(JNIEnv* env, jobject thiz) {
}

的な感じでパッケージ名とメソッド名をconcatしたような名前の関数を定義する方式が一般的に知られていると思われるのですが、Androidのコアパッケージのソースとか色々見ているとNativeMethodsパターンな方式を採用しているのが多い。つまり上記のように定義しないっていうのがAndroidでは一般的なのかなと。んまぁNDKというかこういう辺りな事をやる事自体まったく無いんだけれどもやってみた

Android Live Wallpaper

2013-09-10T00:00:00+00:00 Android Java

いわゆるライブ壁紙っていう機能。SurfaceView的な感じで壁紙に動的にレンダリングしたりとか出来る

まぁ自宅のメモ用ブログには書いてたけどこっちに書いてなかったので、美人時計サービスを利用して1分毎に動的に壁紙をレンダリングするっていうのをやってみる。ただ、結構雑に作ってあるので(ry

※あくまでLive Wallpaperってどんな感じで作るのかっていう

android.intent.action.ASSIST

2013-09-03T00:00:00+00:00 Android

っていうのがあるらしい。ホームボタンからスライドするなりで起動(もしくは一覧から選択して起動)したり出来る模様。おそらくはAPI Level 16から出来るらしいのでAndroid4.1辺りかなと

LocalBroadcastManager

2013-07-19T00:00:00+00:00 Android Java

※ローカルのネタ帳からのほぼコピペ

一言で言えばアプリ内ローカルでのみ利用可能なブロードキャスト的な感じかなと。一般的には

ViewPager(FragmentPagerAdapter)のテスト

2013-07-04T00:00:00+00:00 Android Java

ViewPager(+FragmentPagerAdapter)を使っている際に、Activityのテスト(ActivityInstrumentalTestCase2)でViewPagerで利用されているFragmentPagerAdapterを取って、そのFragmentを取得してごにょごにょテストする方法

ライブラリプロジェクトを伴うAndroidアプリをJenkinsでビルド

2013-06-13T00:00:00+00:00 Android Jenkins

んまぁライブラリプロジェクト自体をJenkinsのビルドプロジェクト(フリースタイル)とかで定義しちゃっても良いとは思いますけど、ActionBarSherlockとかだったりだとか公開されている有用なAndroidライブラリとかを伴うプロジェクトをビルドするとなると定義するかプロジェクト内にぶち込んじゃうかっていう所だと思うのですが、その後者側の「参照するライブラリプロジェクト自体をビルドプロジェクトとして定義せずにアプリプロジェクトにぶち込んでビルド」するっていう方式的なのを検証してみた

Rails(OAuth)+Android SyncAdapter

2013-06-04T00:00:00+00:00 Android Rails

今まで書いたAccountManager及びSyncAdapterなやつは認証トークンを適当に作ってそれを利用してAPIにリクエストを投げるという方式だったのですが、そのサーバー側の問題が色々あるのでそこら辺をRailsで組み直し、Android AccountManager+SyncAdapterではOAuthを使ってAPIリクエストを行う方式というのを採用してみた。要は

BaseAdapterでgetFilter

2013-05-29T00:00:00+00:00 Android Java

BaseAdapterを用いる際にはgetFilterなんてメソッドが存在しないのでどうすんの的な感じなんだけど、Android FrameworkソースのArrayAdapterは

ActionBarSherlockでSearchView

2013-05-27T00:00:00+00:00 Android Java

android.widget.SearchViewなメニューのActionView自体が多分Android3.xくらいからサポートされたんだと思うんだけど、ActionBarSherlockにそれと同等な感じのWidgetが存在する模様。という事でAndroidAnnotations+ActionBarSherlockな構成でやってみた

Androidでmockito+hamcrestを使うとエラーになる件

2013-05-23T00:00:00+00:00 Android

Unable to execute dex: Multiple dex files define Lorg/hamcrest/Description

っていうようにテストを起動したりしようとするとこういうエラーが出ちゃう。その原因っていうが単純でmockitoの中にhamcrestのインターフェース群っぽいのが含まれている模様なので、これのクラスとhamcrestのjarに含まれているクラスが衝突してclasses.dexを生成するにあたってエラーになるんじゃないかと

DialogFragmentのテスト

2013-05-21T00:00:00+00:00 Android Java

例えば、なんかの登録処理をAlertDialogで表示させてsetViewでフォーム的なのをビューとして利用、そのAlertDialog自体はDialogFragmentで生成される。でそれのテストをどうやるのかって所なんですが、DialogFragmentはレイアウトにIDとか含まれず

AddFormDialogFragment fragment = new AddFormDialogFragment();
fragment.show(getSupportFragmentManager(), "add_form_fragment");

的な感じで使ってる場合にそのUIの振る舞いな処理をテストするにはって所なんですが

Androidでテストカバレッジレポートを出力

2013-05-13T00:00:00+00:00 Android Java

http://blog.kawamura-lab.com/?p=68

を参考にしました。まぁ普通に「android update (test-)project」的な事をやったら生成されるbuild.xmlのantタスクでemmaタスクでサポート出来る模様。でこっちでハマったのがライブラリプロジェクトの扱い、ライブラリプロジェクトにもbuild.xmlとかそういう辺りが必要な模様(フィルターすれば必要ないのかは不明)

Universal Image Loader for Androidを使ってみた

2013-05-10T00:00:00+00:00 Android Java

参考: http://qiita.com/chuross/items/e3ca79065d9b67716ace

https://github.com/nostra13/Android-Universal-Image-Loader

ちょうど、AndroidAnnotations+Twitter4jで作ってたデモアプリ的なのがあったので、そいつでプロフィール画像をロードする仕組みな所をこのUniversal Image Loaderを使ってプロフィール画像を読み込むっていうのを処理してみた

Android PullToRefresh

2013-04-16T00:00:00+00:00 Android Java

※以下のAndroid-PullToRefreshは現時点でも既にdeprecatedになっており、 https://github.com/chrisbanes/ActionBar-PullToRefresh を使うべきだそうです。既に補足済み

シリーズネタになるかどうかは現状微妙ですが...

PullToRefreshっていう引っ張ってリロードしたりだとか、画面中でデータが末尾に達した場合に次のページなデータを読み込むかだとか、んまぁそういうのを実現したい時に便利なのが https://github.com/chrisbanes/Android-PullToRefresh っていうのがある訳なんですが、まだ使ってみたりとかした事が無いし、ちょっと要件としてそういうのが出てきたので使ってみた

ActionBarCompat

2013-04-14T00:00:00+00:00 Android Java

一般的にAndroid3.x以下のバージョンにおいてはActionBarそのものがサポートされてない。でAndroidのサンプル集にActionBarCompatっていうのがあって、それを利用する事でAndroid3.x以下のバージョンでもActionBar同様なUIを実現する事は出来る模様(但し、ActionBarクラス自体が存在する訳じゃないのでそれは使えない。あくまでUI的な所かと)

UbuntuとAndroidをbluetooth RFCOMMで通信する

2013-01-21T00:00:00+00:00 Android Java Python

まぁAndroid公式にBluetoothChatっていうのはありますけど。一応Android側はそれのソース読んでやってみたって話。ただこれはAndroidとPC(Ubuntu)をbluetooth RFCOMMを使って通信するというところになる

一応前持って行っておきますが、以下のソースはクソですので... (Threadの扱いとかダメ)。あくまでAndroidとRFCOMMにおける通信方法の手段にしかすぎないかと

でソースですけど、AndroidManifest.xmlとかはBLUETOOTH/BLUETOOTH_ADMINのpermissionが必要なくらいなので端折る。という事でActivityだけ書く

package net.kinjouj.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

public class SampleActivity extends Activity {

    public static final String TAG = "SampleActivity";

    private static final int REQUEST_ENABLE_BT = 2;
    private static final int MESSAGE_CLIENT_ACCEPT = 1;
    private static final int MESSAGE_RECEIVE = 2;

    private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    private BluetoothAdapter adapter;
    private BluetoothSocket socket;
    private ArrayAdapter<String> arrays;
    private AcceptThread mAcceptThread;
    private ConnectThread mConnectThread;

    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_CLIENT_ACCEPT:
                    if (msg.obj != null && msg.obj instanceof BluetoothSocket) {
                        if (mConnectThread != null) {
                            mConnectThread.cancel();
                            mConnectThread = null;
                        }

                        LinearLayout layout = (LinearLayout)findViewById(R.id.layout);

                        if (layout.getVisibility() == View.INVISIBLE) {
                            layout.setVisibility(View.VISIBLE);
                        }

                        BluetoothSocket socket = (BluetoothSocket)msg.obj;

                        mConnectThread = new ConnectThread(socket);
                        mConnectThread.start();
                    }

                    break;

                case MESSAGE_RECEIVE:
                    if (msg.obj != null) {
                        String text = (String)msg.obj;

                        if (!TextUtils.isEmpty(text)) {
                            arrays.add(text);
                        }
                    }

                    break;
            }
        }
    };

    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.main);

        ((Button)findViewById(R.id.btn)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                String text = ((EditText)findViewById(R.id.text)).getText().toString();

                if (TextUtils.isEmpty(text)) {
                    return;
                }

                try {
                    if (socket == null) {
                        Set<BluetoothDevice> devices = adapter.getBondedDevices();

                        if (devices.size() <= 0) {
                            return;
                        }

                        BluetoothDevice device = devices.iterator().next();
                        socket = device.createRfcommSocketToServiceRecord(MY_UUID);
                        socket.connect();
                    }

                    socket.getOutputStream().write(text.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();

                    Toast.makeText(
                        SampleActivity.this,
                        String.format("connect unavailable: %s", socket.getRemoteDevice().getName()),
                        Toast.LENGTH_LONG
                    ).show();
                } finally {
                    if (socket != null) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        arrays = new ArrayAdapter<String>(this, R.layout.messages);

        ((ListView)findViewById(R.id.listView)).setAdapter(arrays);
    }

    @Override
    public void onStart() {
        super.onStart();

        adapter = BluetoothAdapter.getDefaultAdapter();

        if (adapter == null) {
            finish();

            return;
        }

        if (!adapter.isEnabled()) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, REQUEST_ENABLE_BT);
        } else {
            onBonding();
        }
    }

    @Override
    public void onStop() {
        super.onStop();

        if (socket != null) {
            try {
                socket.close();
                socket = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }

        Process.killProcess(Process.myPid());
    }

    @Override
    protected void onActivityResult(int request, int result, Intent intent) {
        if (request == REQUEST_ENABLE_BT && result == RESULT_OK) {
            onBonding();
        }
    }

    private void onBonding() {
        Set<BluetoothDevice> devices = adapter.getBondedDevices();

        if (devices.size() <= 0) {
            return;
        }

        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }

        mAcceptThread = new AcceptThread(adapter);
        mAcceptThread.start();
    }

    private class AcceptThread extends Thread {

        private BluetoothServerSocket server;

        public AcceptThread(BluetoothAdapter adapter) {
            super("accept");

            try {
                server = adapter.listenUsingRfcommWithServiceRecord("RFCOMM Service", SampleActivity.MY_UUID);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            boolean isRunnable = true;

            while (isRunnable) {
                try {
                    BluetoothSocket socket = server.accept();

                    if (socket != null) {
                        managedConnection(socket);
                    }
                } catch (IOException e) {
                    Log.v(TAG, "disconnect", e);

                    isRunnable = false;

                    cancel();
                }
            }
        }

        private void managedConnection(final BluetoothSocket socket) {
            handler.post(new Thread() {
                @Override
                public void run() {
                    BluetoothDevice device = socket.getRemoteDevice();

                    Toast.makeText(
                        SampleActivity.this,
                        String.format("Connect: %s:%s" ,device.getName(), device.getAddress()),
                        Toast.LENGTH_LONG
                    ).show();
                }
            });

            handler.obtainMessage(SampleActivity.MESSAGE_CLIENT_ACCEPT, socket).sendToTarget();
        }

        public void cancel() {
            try {
                server.close();
                server = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class ConnectThread extends Thread {

        private BluetoothSocket socket;

        public ConnectThread(BluetoothSocket socket) {
            super("connect");

            this.socket = socket;
        }

        @Override
        public void run() {
            InputStream is = null;

            try {
                is = socket.getInputStream();

                try {
                    BufferedReader br = new BufferedReader(new InputStreamReader(is));
                    String str = null;

                    while ((str = br.readLine()) != null) {
                        handler.obtainMessage(SampleActivity.MESSAGE_RECEIVE, str).sendToTarget();
                    }
                } catch (IOException  e) {
                    Log.v(TAG, "disconnect", e);

                    cancel();
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        public void cancel() {
            if(socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

簡単に言うと起動するとAcceptThreadが走って、そこでBluetooth RFCOMMをリッスンする。でPC側のクライアントを使って接続するとConnectedThreadが走って、クライアントから受け取ったデータを取得してそれをListViewのアダプターにバインドする。まぁそんな感じ

でPCからデータを送るクライアントは以前書いたネタにも書いてあるけど、python-bluezを使う

from bluetooth import find_service,RFCOMM,BluetoothSocket,BluetoothError

host = None
port = 0

for service in find_service():
    if service["protocol"] == "RFCOMM" and service["name"] == "RFCOMM Service":
        host = service["host"]
        port = service["port"]

if host is not None:
    sock = BluetoothSocket(RFCOMM)
    sock.connect((host, port))

    print "Connected: %s:%d" % (host, port)

    while True:
        data = raw_input("message: ")

        if data is not None:
            try:
                sock.send("%sn" % data)
            except:
                break;

まぁこれを実行してデータを送ったりするとスマフォ側にデータが表示される。でその動画を撮影しました

{% youtube 9FjwF3B0rqo %}

というような感じ。で問題はここからで逆にスマフォ側からPC側にデータをどうやって送るのかって話なんですが。上記のJavaソース上だとcreateRfcommSocketToServiceRecordのメソッドを使ってPC側デバイスのRFCOMMと接続してごにょごにょしているのですが、そもそもPC側のbluetoothにRFCOMMがバインドされてるかって所なんですが、基本的にはRFCOMMは自分でやらないとバインドされないはずなので

sudo sdptool browse local

をやったあとに。Serial Port通信なRFCOMMがあるかどうかを確認しなきゃならん。多分、デフォルトでは無いはずなので

sudo sdptool add --channel=15 SP

で登録しておく。でもっかい見ると

Service Name: Serial Port
Service Description: COM Port
Service Provider: BlueZ
Service RecHandle: 0x10009
Service Class ID List:
  "Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 15

な感じで出てるかと。あとはこっちもサーバープログラムを作って、スマフォから送られてきたデータを取得して表示したりするなりだけ。だが、上記のJavaソースだと一度PC側からクライアント接続しないと送信なフォームが出ないようになってるので、それと事前にPC側のRFCOMMレシーバーなプログラムを起動しておかないとスマフォ側からデータ送信出来ない。理由としてスマフォからPC側のサーバーへのコネクションは一度処理した一回のみだけ接続処理を行うので

でそのRFCOMMサーバープログラムも書く。ここもPython

from bluetooth import BluetoothSocket,RFCOMM
import notify2

port = 15

server = BluetoothSocket(RFCOMM)
server.bind(("", port))
server.listen(1)
conn, addr = server.accept()

notify2.init("bluetooth notify")

while True:
    data = conn.recv(4096)

    n = notify2.Notification("Bluetooth Message", data)
    n.show()

っていう感じ。今回のだと

  • PC側のRFCOMMサーバーを起動する (その前にPC側でRFCOMMなSerial Portあるか確認)
  • スマフォアプリ側を起動
  • PC側クライアントからスマフォのRFCOMMへ接続するプログラムを実行 (ここで双方的なコネクションが立つ)
  • PC側からデータを送る (スマフォアプリ側の画面に出る)
  • スマフォ側からデータを送る (PC側にinoitfyなポップアップが出る)

Android AccountManagerで使えるカスタムアカウントを開発する方法

2013-01-19T00:00:00+00:00 Android Java

んまぁAndroid端末使ってる人とかだと見た事あるし、触った事もある人もいるだろう

まぁWebサービスとかのアカウントを端末内に保管して、認証要求だとかをAccountManagerな機能を通じて行えたり出来るAPIが存在する訳ですが、これの独自のを作る方法。まぁ結構書く量が多いので

AppWidgetでListView

2013-01-18T00:00:00+00:00 Android Java

検証したのいつかは覚えてないんですが、ネタとして書いておく

Android API Level 11(Android3.x)くらいから(RemoteViews.setRemoteAdapter辺りがAPI Level14が必要なのでAndroid4.0以降)だと思うんですが、AppWidgetにListViewが使えるようになってる。それを検証したという件

PagerTitle(Tab)Stripを使ってみる

2012-10-15T00:00:00+00:00 Android Java

最近、サーバー関係なタスクが多くてAndroidな音沙汰がまったくしなかったのでリハビリ的な感じでちょいと調べ物をしてました。それがだいぶ前からGoogle Play等であったように

の上部に表示される「おすすめ」や「カテゴリ」といったようなヘッダーラベルのような物。これどうやってやるのかって調べたらPagerTitleStrip、PagerTabStrip等を用いる事で簡単に出来る模様。なのでやってみた(実行環境はAndroid2.2)

(AndroidManifest.xmlはアクティビティ定義だけなのでパス)

android.accessibilityservice.AccessibilityServiceを使用してNotificationを取得する

2012-10-14T00:00:00+00:00 Android Java

ちょっと前にDeskNotifierっていうスマフォの通知をPCにプッシュするというアプリがあったのを見て思ったのですけど、(Android)スマフォのNotificationって監視して取得したり出来る物なのかっていう所が不明だったので色々と調査

で調査結果としてAccessibilityServiceを使う事で可能な模様、という事でやってみた。まぁ詳しい事は公式ドキュメントに書いてあるんですけど

ConnectivityManager.isActiveNetworkMeteredな件

2012-10-08T00:00:00+00:00 Android Java

なんかどっかで見た気がするのですが、例えばでかいファイルをダウンロードする必要性がある場合に3GではなくWi-Fiを使ってダウンロードさせたい。っていうか正しいのかは曖昧なんですが、「通信制限が存在するネットワークでダウンロード処理を継続しない」っていう処理をする場合等に使える模様。

android.support.v13.dreams.BasicDream

2012-10-07T00:00:00+00:00 Android Java

android-support-v13.jarの中身を見てたらそういうのがあるっていうのを発見。で色々調べてみたら、なにやらスクリーンセーバーのような物を作れるAPIな模様。という事でやってみた

android.widget.ArrayAdapterでのフィルタリングの拡張

2012-02-28T00:00:00+00:00 Android Java

Quick Search Box関係を調べていて勉強しているとふと思ったのが、クエリーによる検索をするのは良いけど例えばフィルターをする実際の処理を自分で実装した場合とかどうするのって思った。というかどうやらデフォルトのAdapterのフィルターだと検索キーワードをトークンで分割してフィルタしてくれない模様なんだけど。という事でフィルタ処理を自分で処理する方法を調べた所だと、Adapterとそれが使用するFilterオブジェクトを拡張してやれば良いらしい

android.content.BroadcastReceiver Using Android ICS of android.test.AndroidTestCase

2012-01-07T00:00:00+00:00 Android Java

Android4.0以前だとonReceiveメソッドをそのままインスタンスメソッドとしてAndroidTestCaseを使ってテスト出来たんですけど、Android4.0からだとエラーになる。そのエラーになるケースがsetResultExtrasを使った場合になるんですが[APIドキュメント][1]によると

Change the current result extras of this broadcast; only works with broadcasts sent through Context.sendOrderedBroadcast. This is a Bundle holding arbitrary data, whose interpretation is up to the broadcaster. Can be set to null. Calling this method completely replaces the current map (if any). This method does not work with non-ordered broadcasts such as those sent with Context.sendBroadcast

って書いちゃってんすよね。英語読める訳じゃないけど、要するにsetResultExtrasを使う場合はonReceiveをそのまま実行してもsendBroadcastでやっても出来ないらしいっぽい事を書いてる気がする。じゃあどないすんねんと。まぁ書いてあるとおりにsendBroadcastではなく、sendOrderedBroadcastを使えとの事らしいので

android.content.Loader Test Case Using android.test.LoaderTestCase

2011-12-19T00:00:00+00:00 Android Java

例えば

SampleLoader.java

package sample.test;

import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
import android.database.MatrixCursor;

public class SampleLoader extends CursorLoader {

    public SampleLoader(Context context) {
        super(context);
    }

    @Override
    public Cursor loadInBackground() {
        Cursor csr  = null;

        try {
            MatrixCursor mc = new MatrixCursor(new String[]{ "_id", "NAME" });
            mc.addRow(new Object[] { 1, "hoge" });
            mc.addRow(new Object[] { 2, "fuga" });
            mc.addRow(new Object[] { 3, "foobar" });

            csr = mc;
        } catch(Exception e) {
            e.printStackTrace();
        }

        return csr;
    }
}

的な感じで作っといて、これをテストする方法ですが。普通にLoaderTestCaseっていうのがあるので