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

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

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

2013年元旦。明けましておめでとうございます。

明けましておめでとうございます。
f:id:begirama:20130107141556j:plain

年末年始は、ゆっくりと娘と粘土や、人生ゲームをやって平和な正月でした。
昨年は2度目の転職など、公私ともに変化が多かった年でしたが、
今年はデザイン、プログラム、新企画など集中と、継続の年。

本年もよろしくお願い致します。

f:id:begirama:20130107141049j:plain
自分で作って気に入った白ヘビとドロイド君。

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;

以上です。

Mac OS X 64bitでSOAP UIを起動させるために

切っ掛けは不明ですが、数ヶ月前は普通に使えていたSOAP UIが起動できなくなってしまいました。
OSのバージョンを上げたときに、使ってるJVMが変わったかな?OSのバージョンはOS X 10.8.2。

エラー内容
こんなエラーがでます。
java.lang.Exception: Object owner or JVM pointer are not correct

$soapui.sh 
================================
=
= SOAPUI_HOME = /Applications/soapui-4.5.1
=
================================
Configuring log4j from [/Applications/soapui-4.5.1/bin/soapui-log4j.xml]
10:54:33,746 INFO  [DefaultSoapUICore] initialized soapui-settings from [/Applications/soapui-4.5.1/soapui-settings.xml]
10:54:34,667 INFO  [WorkspaceImpl] Loading workspace from [/Applications/soapui-4.5.1/bin/../../TestPBE-workspace.xml]
10:54:34,753 INFO  [WsdlProject] Loaded project from [file:/Users/sogo/Documents/soapui/NewWSDLFile-soapui-project.xml]
10:54:35,229 INFO  [SoapUI] Used java version: 1.6.0_37
objc[38025]: Class MessageLoopView is implemented in both /private/var/folders/xp/3j0nqcx93sbgs2zxszylhcnh0000gn/T/jxbrowser-3.0.Build.Unknown/libjniw.jnilib and /private/var/folders/xp/3j0nqcx93sbgs2zxszylhcnh0000gn/T/jxbrowser-3.0.Build.Unknown/libjniwrap.jnilib. One of the two will be used. Which one is undefined.
objc[38025]: Class MessageLoopView is implemented in both /private/var/folders/xp/3j0nqcx93sbgs2zxszylhcnh0000gn/T/jxbrowser-3.0.Build.Unknown/libjniw.jnilib and /var/folders/xp/3j0nqcx93sbgs2zxszylhcnh0000gn/T/jxbrowser-3.0.Build.Unknown/libjniw.jnilib. One of the two will be used. Which one is undefined.
2012-12-14 10:54:37.717 java[38025:11703] [Java CocoaComponent compatibility mode]: Enabled
2012-12-14 10:54:37.742 java[38025:2517] java.lang.Exception: Object owner or JVM pointer are not correct

解決策
エラー内容からUIの部分が64bitがサポートされてないみたいなので、Javaの起動オプションに-d32を追加すればOKです。

soapui.shを開いて、JAVA_OPTSを探します。

JAVA_OPTS="-Xms128m -Xmx1024m -Dsoapui.properties=soapui.properties -Dsoapui.home=$SOAPUI_HOME -d32"

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

スクラム道EXPO 2012に参加しました。

スクラム道EXPO 2012に参加してきました。

テーマはこの6つでした。

  1. ペアプログラミング ホントのところ
  2. Doneの定義 虎の巻
  3. インセプションデッキ
  4. スクラムマスター思い出語り
  5. 分散スクラム
  6. 自律型組織


個人的には、今まさにコンサルで入っているプロジェクトで、初めてアジャイルに取り組むチームが半年弱試行錯誤してきて、次のステップとして自律型組織を目指すべきでは無いかと考えていましたので、ばっちりのテーマでした。


特に優秀であるほど、PM、リーダクラスは責任感が強く、いい加減な仕事はできないという気持ちが強いため、逆に彼らの確認待ち、計画待ちになることもフェーズによっては有り得ます。もっと大きなプロジェクトに、今試行している開発プロセスでチャレンジするためには一度、リーダ層のタスクを洗い出して、メンバーに任せられるもの、自動化できるものを分別しようと思います。そうしないとスケールしないですね。


Doneの定義についても、一応あるものの、これまでのやり方に従うところが多かったので、一度、振り返りをして足す物、引く物を検討すべきだと気づきがありました。


参考文献
塹壕より Scrum と XP