そごうソフトウェア研究所

SOA、開発プロセス、ITアーキテクチャなどについて書いています。Twitterやってます@rsogo

日本Androidの会の2013年6月定例会に行ってきた

日本Androidの回に行ってきました。
https://www.android-group.jp/event/event19.html

内容は下記でした。
1.HTML5最新動向: オフラインで動くPackaged Web Appのクラウド利用
2.Android最新動向: Google GlassのUI/UXとGlass Wareの作り方
3.Android最新動向: Google IO報告 & Android Studio

会場はデジタルハリウッドさん。

1.HTML5最新動向: オフラインで動くPackaged Web Appのクラウド利用


Packaged Web AppはHTML5で作るんだけど、オフラインでも動くっていうのが当たり前。
一方で、クラウドと連携するのが当たり前。


先生の資料でもchromeのパッケージなんかを使っている部分もあって、やっぱり作る側からすると多くのユーザに使ってもらいたいという思いがあるから、どのブラウザでも動いて欲しい。一方で先進的な機能は一部のブラウザでしか動かなかったりというジレンマがある。
まあ、Chromeだったらスマフォでも、PCでも動くというのは、それはそれでありかも知れない。


この辺のサーバとのデータの同期周りは自分で開発しているので、それが仕組みとして提供されたら便利。

2.Android最新動向: Google GlassのUI/UXとGlass Wareの作り方

こちらはオフレコとのことで、省略。
面白いねぇ、早く欲しいねぇ。

3.Android最新動向: Google IO報告 & Android Studio


海外のエンジニアはIntelliJ好きだね。
Android Studioはまだまだバグもあるし、実プロジェクトならEclipseだろう、というお話。チーム人数多くて、開発環境を標準化するならEclipseだろうね。
逆に数人でやるなら、Android Studioを試してみるのもありかも。

Googleクラウドプリントを非対応プリンタで試してみた

Android端末から直接印刷を使いたい!ということでGoogleクラウドプリントを試してみました。


GoogleクラウドプリントはGoogleのアカウントを持っていれば、AndroidやChromebook、もちろんPCやMacからもGoogleのサーバを介して対応したプリンタへ印刷ができるサービスです。

f:id:begirama:20130201013943p:plain

ところが、Googleクラウドプリントに対応したプリンタはEpsonやCanonからも出ていますが、まだまだオフィスで置いてあるような業務プリンタは対応してない。そこで、今回はGoogleクラウドプリントに対応していない普通のページプリンタを使って検証を行っています。


非対応プリンタをGoogleクラウドプリントで使うためには、仲介となるChromeがインストールされたPCが必要になります。今回はOS X 10.8.2のMacBookAirを使いました。


このChromeの設定から、Googleアカウントとプリンタのひもづけを行います。プリンタの情報が事前にPCに設定されていればクリックだけで設定できます。
設定の詳しいやり方が日本語マニュアルで公開されていて、10分くらいでできました。
https://support.google.com/cloudprint/answer/1686197?rd=1


Android側はGalaxy S3にCloud Printを入れて試しました。

印刷の仕方も簡単で、アプリ上で印刷するファイルを指定して、Googleアカウントにひも付いたプリンタから使用するものを選びます。


この時、注意が必要なのは仲介となるChromeがインストールされたPCが起動してないといけないということ。まあ、Android端末は社外のネットワークで直接プリンタと接続できないのだから、仲介となるPCが動いていなければ無理ですね。


その場合、印刷ジョブはGoogle側でキューイングされるので、アプリ側の印刷依頼自体は完了します。こちらは印刷依頼が終わった後の画面。QUEUEDというステータスになってますね。
f:id:begirama:20130131204119p:plain


仲介PCが起動していないといけないのはハードルが高いので対応プリンタが増えて欲しいところですが、どんなときにこのサービスが役に立つのでしょうか。
プリンタに接続されたPCが使える環境ではあまり意味のないサービスですから、出張先の支社や客先でプリンタの設定をせずにちょっと印刷したい、逆にお客さんに一時的に使わせたい、スマホやタブレットだけ持って営業所に行って印刷したい、タブレットでお医者さんが患者さんにヒアリングして結果を印刷して渡してあげる、といった普段のオフィスとは違う環境で印刷したい時には良いのではないのでしょうか。


もちろん開発者向けにAPIがありますから、アプリに組み込むことが可能です。
https://developers.google.com/cloud-print/?hl=ja

Androidとの連携はこちら。
https://developers.google.com/cloud-print/docs/android

Android エミュレータのSQLiteのデータを見るには

SQLiteを使うAndroidアプリを作って、エミュレータデバッグしているときに、データの中身を見たくなりますよね。


2つの方法があります。
1つはdbファイルを取り出して、PC側でSQLiteのクライアントツールを使って読み込む。
もう一つはadbでエミュレータにアクセスして、直接SQLを実行する方法です。ここでは2つ目の方法を書こうと思います。理由はいつも忘れて、調べるのが面倒だからです・・・。


SQLiteのデータ・ファイルはここにあります。

/data/data/<package_name>/databases/<database_name>


adbを起動してエミュレータの中に入っていきます。

$ adb shell
# cd /data/data/com.nextconceptdc.std/databases
# ls
sample.db

# sqlite3 socialToDo.db

(ここからはSQLiteの中です)

SQLite version 3.7.4
Enter ".help" for instructions
Enter SQL statements terminated with a ";"


上の説明の通り、".help"コマンドでヘルプが見れます

sqlite> .help
.backup ?DB? FILE      Backup DB (default "main") to FILE
.bail ON|OFF           Stop after hitting an error.  Default OFF
.databases             List names and files of attached databases
.dump ?TABLE? ...      Dump the database in an SQL text format
                         If TABLE specified, only dump tables matching
                         LIKE pattern TABLE.
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program
.explain ?ON|OFF?      Turn output mode suitable for EXPLAIN on or off.
                         With no args, it turns EXPLAIN on.
.header(s) ON|OFF      Turn display of headers on or off
.help                  Show this message
.import FILE TABLE     Import data from FILE into TABLE
.indices ?TABLE?       Show names of all indices
                         If TABLE specified, only show indices for tables
                         matching LIKE pattern TABLE.
.load FILE ?ENTRY?     Load an extension library
.log FILE|off          Turn logging on or off.  FILE can be stderr/stdout
.mode MODE ?TABLE?     Set output mode where MODE is one of:
                         csv      Comma-separated values
                         column   Left-aligned columns.  (See .width)
                         html     HTML <table> code
                         insert   SQL insert statements for TABLE
                         line     One value per line
                         list     Values delimited by .separator string
                         tabs     Tab-separated values
                         tcl      TCL list elements
.nullvalue STRING      Print STRING in place of NULL values
.output FILENAME       Send output to FILENAME
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.restore ?DB? FILE     Restore content of DB (default "main") from FILE
.schema ?TABLE?        Show the CREATE statements
                         If TABLE specified, only show tables matching
                         LIKE pattern TABLE.
.separator STRING      Change separator used by output mode and .import
.show                  Show the current values for various settings
.stats ON|OFF          Turn stats on or off
.tables ?TABLE?        List names of tables
                         If TABLE specified, only list tables matching
                         LIKE pattern TABLE.
.timeout MS            Try opening locked tables for MS milliseconds
.width NUM1 NUM2 ...   Set column widths for "column" mode
.timer ON|OFF          Turn the CPU timer measurement on or off


今の環境設定を見てみます。

sqlite> .show
     echo: off
  explain: off
  headers: off
     mode: list
nullvalue: ""
   output: stdout
separator: "|"
    stats: off
    width: 


テーブルの一覧を見てみます

sqlite> .tables
android_metadata  auth              task           


taskテーブルをSELECTしてみます。カラム名とかが表示されないので分かりにくいですね。

sqlite> select * from task;
|rr||false||||||||1||||rr||

ヘッダーが表示されるように設定します

sqlite> .headers ON


環境設定を見てみると、headersがonになっています

sqlite> .show   
     echo: off
  explain: off
  headers: on
     mode: list
nullvalue: ""
   output: stdout
separator: "|"
    stats: off
    width: 


もう一度、SELECTをやってみます。今度はカラム名も出てくれました。

sqlite> select * from task;
startDate|detail|status|isUploaded|ownerId|finishDate|projectId|rejectDate|deleted|id|completeDate|_id|created|updated|acceptDate|name|duedate|currentUserId
|rr||false||||||||1||||rr||


テーブルの定義も見たいですよね。Oracle DBでいうdescです。

sqlite> .dump task
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE task(startDate datetime,detail text,status integer,isUploaded string default 'false',ownerId integer,finishDate datetime,projectId integer,rejectDate datetime,deleted datetime,id integer,completeDate datetime,_id integer primary key autoincrement,created datetime,updated datetime,acceptDate datetime,name text,duedate datetime,currentUserId integer);
INSERT INTO "task" VALUES(NULL,'rr',NULL,'false',NULL,NULL,NULL,NULL,NULL,NULL,NULL,1,NULL,NULL,NULL,'rr',NULL,NULL);
COMMIT;

以上です。

Android。フラグメントでアクションバーを使う。

コード側のポイントは3つだけです。
1.onCreateViewの中でsetHasOptionsMenu(true)でオプションメニューを有効にして
2.onCreateOptionsMenuでメニューの内容の指定
3.onOptionsItemSelectedでメニューを選択されたときの処理を実装

public class TaskListFragment extends Fragment {
	
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
       setHasOptionsMenu(true);
       return inflater.inflate(R.layout.tasklist_fragment, container, false);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {

        inflater.inflate(R.menu.activity_std_main, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Toast.makeText(getActivity(), "Selected Item: " + item.getTitle(), Toast.LENGTH_SHORT).show();
        return super.onOptionsItemSelected(item);
    }
}

メニューの内容はres/menu/の下にxmlで指定。

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_refresh"
        android:title="@string/menu_refresh"
        android:icon="@drawable/std_button_24"
        android:showAsAction="ifRoom" />
    <item android:id="@+id/menu_project"
        android:title="@string/menu_projectlist"
        android:icon="@drawable/std_button_26"
        android:showAsAction="ifRoom" />
    <item android:id="@+id/menu_today"
        android:title="@string/menu_today"
        android:icon="@drawable/std_button_28"
        android:showAsAction="ifRoom" />
    <item android:id="@+id/menu_new_task"
        android:title="@string/menu_new_task"
        android:icon="@drawable/std_button_30"
        android:showAsAction="ifRoom" />
</menu>


後はマニフェストファイルのandroid:uiOptionsで、ユーザの環境に合わせてどんな見せ方をしたいのかを指定。splitActionBarWhenNarrowを指定した場合は、アクションバーに収まりきらない場合は、スプリットアクションバー(最下部に表示されるバー)に配置するという指定。

<activity
    android:name="com.nextconceptdc.std.ui.TaskListFragment"
    android:label="@string/title_activity_std_main"
    android:uiOptions="splitActionBarWhenNarrow" >
</activity>

splitActionBarWhenNarrowを指定した場合
f:id:begirama:20121203102928p:plain


splitActionBarWhenNarrowを指定しない場合。入る分は最上部のアクションバーに配置され、それ以外はメニューからドロップリストとして表示される。
f:id:begirama:20121203102934p:plain

アプリ内での複数テーブルへの問い合わせ

本エントリでは、Androidアプリで複数のテーブルを結合して情報を取り出すことについて書こうと思います。

AndroidではデータベースとしてSQLiteが用意されています。
業務系のアプリを作るような場合は特にデータの生成、管理がメインの処理になる傾向があるので、SQLiteを使うことも多いと思います。


そこで、テーブルの設計をやることになりますが、正規化を進めていくうちに画面に表示したい項目が複数のテーブルにまたがることも良くあります。


自分の場合、Androidアプリで曖昧な検索を複数テーブルに対して行うことはあまりありません。曖昧な検索とは、社員テーブルに佐藤さんを条件で、売上テーブルに売上100万円以上を条件で両方にヒットしたものを画面に表示するようなものです。理由はおそらく2つ。

1つは、スマホは画面が小さいのでその画面で検索結果が大量にあってページ送りを何度もしないとデータが見れないような画面設計をやらない。これはタブレットになってくるとそんな要件の画面もでてくるのかも知れません。

2つ目は、複数のテーブルの曖昧検索をしたいような場合ってほとんどが分析や一覧用途だと思います。分析したくなるほどのデータががAndroid端末のストレージに蓄積されていることって少ないんじゃないでしょうか。そのような場合、おそらくバックエンドのサービスにデータは蓄積されていて、分析したい場合はバックエンドに問い合わせるでしょう。


複数テーブルに問い合わせるケースで良くあるのが、社員マスタに社員番号と、社員名があり、売上テーブルかには社員番号と社員毎の売上があるような場合です。売上の上位5件を表示したときに社員番号だけではなく社員名も表示したいですよね。


そのような時、社員マスタと売上テーブルを結合してデータを取ってきたくなりますが、どうやってますか?
1つはSQLiteDatabase.rawQuery()を使って、自分でSELECT文を書いてしまうやり方です。これはSQLを書けるので、自由になんでも書けます。

もう一つはSQLiteDatabase.query()を使うやり方ですが、引数にテーブル名を渡す際に、カンマ区切りで複数のテーブルを指定し、Selection(SQLのWhere句に該当)の引数にテーブルを結合するための条件を書けます。すごい端折って書くとこんな感じです。


ContentProviderを呼び出してる方

String[] columns = new String[3];
columns[0] = "RevenueTable.EmpNo";
columns[1] = "EmploeeTable.EmpName";
columns[2] = "RevenueTable.Revenue";

String selection =  "EmploeeTable.EmpNo=RevenueTable.EmpNo";
				
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(uri,  columns, selection, null, null);


ContentProvider側

public Cursor query(Uri uri, String[] projection, String where, String[] whereArgs,String order) {

	Cursor cursor = db.query(
			 "RevenueTable, EmploeeTable",
			projection,
			where,
			whereArgs,
			null, null, order);
	return cursor;
}

参考にさせていただいた記事
Androidアプリのデータ保存方法の一つ「SQLite」の使い方 レコード検索編

Best way to join tables using sqlite in android

SQLiteで外部キー制約

http://stackoverflow.com/questions/2421189/version-of-sqlite-used-in-android

Androidアプリでカメラ機能を使ってスマホならではの便利さを。

便利系のアプリでカメラの機能を使うというのは結構良くあるんじゃ無いでしょうか。
例えば、Evernoteの様に何かをメモする、記録する。
Facebookのように写真をアップして、みんなに共有する。


企業内で使われるようなアプリでも現場の写真や、名刺の写真を作業報告書、日報に添付するような用途も考えられます。

f:id:begirama:20121026173912p:plain


このようなカメラ機能を使って日頃の生活や、仕事が便利になるアプリはGPSと同様、今まであったWebアプリではできないスマホならではの差別化がし易いところです。


さて、Androidアプリではカメラ機能を使うために2つの方法があります。1つは、提供されているカメラ用のAPIを使って自分で実装する。もう一つは、Intent(MediaStore.ACTION_IMAGE_CAPTURE)でAndroid端末に用意されているカメラアプリを呼び出して、カメラ機能はそのアプリに任せてしまうことです。


カメラ専用アプリなど、写真をとること自体に独自性を持たせたい場合は、カメラAPIを使う必要がありますが、そうでは無い場合、Intentを使って標準カメラアプリを使うことをお勧めします。
理由はカメラAPIを使った場合、端末独自の仕様によって上手く動かない、端末毎のテストが増えるからです。Android端末の標準カメラアプリに任せれば、各端末でテスト済みでしょうから安心できますね。


Intentを使ってカメラアプリを呼び出した場合、その結果は次のどちらかで受け取ることができます。

  • Extraでビットマップを直接受け取る
  • カメラアプリを呼び出す時に保存場所を場所を指定し、コールバックがあった時にその場所の写真を取得する


実際のコードはこちらのサイトが非常に丁寧で分かりやすかったです。4.0.3でも動作確認できました。
http://9ensan.com/blog/smartphone/android/android-camera-intent/

Androidでデータを処理するライブラリを調べる

今日はAndroidアプリが使っているライブラリについて調べてみました。

なんでこれを調べたかというと、Androidプラットフォームを素で使用してアプリを開発していると、いくつかの今時のアプリ開発で良くある機能が欠けていると気づくことがあります。


例えば、リレーショナルDBとJavaのObjectをマッピングしてくれるORマッパー。SQL書いて、返ってきたCursorからデータを取っていちいち変数にセットするなんてめんどくさい!

他には、データと、UIコンポーネントバインディングAndroidでもSpinnerや、Listは自分で拡張すればCollectionをがさっと渡せば表示用に値をセットできたりしますが、他のJava EE, .Net等のプラットフォームでは画面の一つ一つの項目までレイアウトファイルに埋め込まれたIDを使って勝手に変数の値を表示用にひも付けてくれます。


ゲームアプリなどではこれらの機能はあまり使わないかも知れませんが、企業向けのアプリなどでは画面の表示項目が多かったり、データベースを使用するのでUIコンポーネントから変数へ、変数からSQLへといった単純な変数操作を何度もやることになります。
こういう問題を解決してくれるライブラリが既にあるんのではないか?という仮説のもと、公開されているアプリケーションが使用しているライブラリを調べました。


使用されているライブラリの統計情報を公開しているのはApp Brainです。ここから情報が参照できます。


やはり、多いのはGoogle Support Library。Fragmentを2.3系のAndroidで使おうと思うと必須。
次に多いのはユーザの操作を分析するもの。


AndroidSQLiteでORマッピングをやってくれるOrmLite等が非常に下位にランキングされていましたが、企業向けは外部に公開しないので、この統計には表れないのかも知れませんね。