0

AndroidScala用iBeaconライブラリに領域判定機能つけた

だいぶ書き忘れてたけど3週間ぐらい前に作った気がする

https://github.com/shokai/iBeacon-AndroidScala


前:AndroidでiBeaconを読む(Scalaで)


onRegionでRSSIの範囲を指定してevent登録できる
-70から-50、端末にもよるけどだいたい15メートル以上20メートルぐらいに入った時にcallbackされる。

// UUID, major, minor, RSSI range, callback
iBeacon.onRegion("805D6740-F575-492A-8668-45E553EB9DF2", "0001", "0001", Range(-70,-50), (beacon:Beacon) => {
Log.v("iBeacon", s"region(-70~-50) UUID=${beacon.uuid} RSSI=${beacon.rssi}")
})

UUID, major, minorはそれぞれnullを与えると何にでも反応するようになる

onRegionのラッパーでonFar, onNear, onImmediateというのがあって、それぞれ電波とどかなくなるギリギリ30メートルぐらい、割と近い数メートル、すごく近い数十センチでcallbackする。
iBeacon.onFar("805D6740-F575-492A-8668-45E553EB9DF2", "0001", "0001", (beacon:Beacon) => {
Log.v("iBeacon", s"far: ${beacon.uuid}")
})

iBeacon.onNear("805D6740-F575-492A-8668-45E553EB9DF2", "0001", "0001", (beacon:Beacon) => {
Log.v("iBeacon", s"near: ${beacon.uuid}")
})

iBeacon.onImmediate("805D6740-F575-492A-8668-45E553EB9DF2", "0001", "0001", (beacon:Beacon) => {
Log.v("iBeacon", s"immediate: ${beacon.uuid}")
})

これらを使って研究室のドアに近づいたらカギが開くようにしたりしてる。

0

AndroidでiBeaconを読む(Scalaで)

前:ScalaでAndroidアプリを作る


iBeaconをAndroidで読んで、ビーコンが現れたらNotificationを出すとかできるライブラリを作った。
advertiseパケットを自前でparseしてiOSのSDKで使われてるのと同じフォーマットの16進コードに変換してるけど、scalaなので15行ぐらいで書けた。

https://github.com/shokai/iBeacon-AndroidScala

先週研究会でspammerっていう人がiBeaconの仕組みとかをまとめて発表してくれた。
へぇーと思ったのでMyBeacon買って土日にAndroid用のライブラリを作った。

Javaは冗長でつらいのでScalaで書いたけど、Scalaもライブラリの配信方法が.classをjarに固めてmaven使うとか冗長でつらい。どうすればいいんだ。なんでコンパイル前のscalaスクリプトのまま流通させないのか理解できない。


使い方


詳しくはGitHubのREADMEに書いた。

Activityから起動する例
import org.shokai.ibeacon.{IBeacon, Beacon};

class MainActivity extends Activity{

lazy val iBeacon:IBeacon = new IBeacon(this)

override def onCreate(savedInstanceState:Bundle){

// ビーコンを発見した時にcallbackされる
iBeacon.onDiscover((beacon:Beacon) =>
Log.v("iBeacon", s"UUID=${beacon.uuid} Major=${beacon.major} Minor=${beacon.minor} RSSI=${beacon.rssi}")
)

}

}


AndroidManifest.xml
<manifest>
<application> ~~ (略) ~~ </application>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
</manifest>

とても簡単でよい。
onBeaconというコールバックもあって、これは全てのビーコンパケットをコールバックしてくる。
ビーコンは常に電波を発信しまくっているので、onBeaconイベントはものすごい勢いで呼ばれる。
コールバックされるBeaconオブジェクトのrssiプロパティで受信信号強度が取れるので、距離もなんとなく計れる。
だいたい見通し25メートルぐらいで-90、接触するぐらい近いと-20〜-10とかになる。

なお1度呼ばれた同じビーコンのonDiscoverが再び呼ばれるのは、そのビーコンの信号が5秒間無くなって再び現れた時。


サンプルアプリ


信号を読むやつapk


ビーコンを見つけたら通知を出すサービス(電源ONしたら自動起動する)

これ改造すると、猫にビーコンつけて近寄ってきたらエサくれ通知を出すとか、自分が家に近づいたらカギが開くとか簡単に作れる。


iBeaconとは

Appleが考えたBluetoothデバイスの存在通知手法で、ずっと電波出し続けているデバイス(ビーコン)をいろいろな場所に設置しておいて、その電波をスマホが検知すると「〜〜というデバイスがあるのでアプリを起動しますか?」のような通知を出す。
最近のiOSアプリ持ってないので実物を見たことがないんだけどそんな感じのはず。

実装としてはBluetooth LE (ローエナジー) のadvertiseパケットの末尾にビーコンのUUIDやmajor,minorといった識別子を突っ込んでいるもので、そんなに凄い事をしているわけではない。
なのでAndroidでもadvertiseパケットを見て、バイナリをparseしたらUUIDとか取り出せた。

インスタントメッセンジャーアプリの名前欄で会話してる人とかいるけど、そういうのに近い。


1800円のMyBeaconを3個買って、研究室に設置した。


参考

iOS – AndroidでiBeacon信号を受信してみよう – Qiita

0

ScalaでAndroidアプリを作る

普段Javaで書いている部分をScalaで書けるようになった。
Javaだと、文字列や複雑なデータ構造を処理したり、関数プログラミングがしづらい。lambda無いし。そういう部分だけでもScalaでやりたい。

https://github.com/pfn/android-sdk-pluginというフレームワークを使って、既存のAndroidアプリのプロジェクトでScalaとJavaを混ぜこぜに書けるようにする方法を説明する。

全体の簡単な手順

  1. homebrewでscalaとsbtをインストールする
  2. Androidのプロジェクトを普通に作る or 既存のAndroidプロジェクトを用意する
  3. sbtで使う設定ファイル3つを配置する
  4. sbt初回起動時に必要なライブラリがインストールされて、Androidプロジェクトとsbtプロジェクトが共存した状態になる
  5. sbtでandroid:packageすると、1つのapkになる

という感じ。


参考にしたサイト

この2つしか読んでいない。
もし俺の記事でよくわからない箇所があったら、下手にググって余計なの読む前に下の2つをちゃんと読んだ方がいい。
今回使っている「android-sdk-plugin」と似たような位置づけのフレームワークとして、「android-plugin」と「scaloid」がある。
前者は少し古い物でこれを参考にandroid-sdk-pluginが作られた。
後者はandroid-sdk-pluginの上にさらに乗せるライブラリで、XMLでのレイアウトをscalaだけで書くようになっていたりしていて、既存のAndroidプロジェクト内でScalaも使うという用途にはちょっと向いていないと思った。

このように似たライブラリがあるので、安易に「scala android」とかでググると関係ない情報を読むことになる。公式ドキュメント重要。


試しに作ったもの

ここに置いた
https://github.com/shokai/ScalaAndroidTestApp
「つらい」が「っらぃ」のようになるだけのアプリ。
Scalaっぽい(Javaでは書きにくい)文字列やMapの処理と、Javaで書かれているAndroid SDKのGUI機能両方を使ってみたかったのでやってみた。
元ネタはScalaをちょっと勉強したに書いた研究室の練習問題。

MainActivityのコードを見ればわかるが、scalaだと辞書作って1文字ずつチェックして置換するような処理はJavaよりも書きやすい。


Androidアプリの新規作成

Android開発環境をインストールしなおしたに書いた手法でHomebrewでAndroid SDKをインストールした。
Eclipseは入れていない。emacsとterminalだけで開発する。

brew install android-sdk


まずプロジェクトのビルドターゲットを確認してから
android list
id: 3 or "android-18"
Name: Android 4.3
Type: Platform
API level: 18
Revision: 2

新規プロジェクトを作る。とりあえずビルド、端末にインストールまでする
android create project --target "android-18" --name ScalaTestApp --path `pwd` --activity MainActivity --package org.shokai.scalatestapp
android update project --path `pwd`
ant debug
adb install -r bin/ScalaTestApp-debug.apk


Scalaが使えるようにする

antの代わりにsbtを使う。

brew update
brew install scala sbt
scala 2.10.2 と sbt 0.13.0 がインストールされた。
sbt(simple-build-tool)は名前に反してシンプルではなく、node.jsのnpmに近い多機能な物で、
  • main()があるファイルを適当に探して、実行してくれる
  • scalaのライブラリのパッケージ管理、バージョン固定
  • 実行するscalaインタプリタのバージョン指定
  • ScalaとJavaを共存させ、.classなどの位置を気にせずに混ぜこぜで実行できる
  • Javaの場合は.classなどの中間ファイルも適当な場所に生成、管理してくれる
  • コマンド名の頭に「~ 」(チルダスペース)を付けると、ソースファイルの変更を検知してコマンドを再実行してくれる
などの機能がある。
いくつかはscala android-sdk-pluginがやってくれている機能なのかもしれない。
まだsbt使い込んでいないから完全に把握しきれていない。

sbt自体がなかなか面白いプロダクトなので、Getting Started — sbt Documentationを読んだ方がいい。


Androidプロジェクト内にファイルを3つ置いてsbt起動すればscalaが使えるようになる。
それぞれのファイル内でAndroid SDKやsbtのバージョンを指定しているので注意。

build.sbt (1行ずつ空けなければならないらしい)
import android.Keys._

android.Plugin.androidBuild

name := "ScalaTestApp"

platformTarget in Android := "android-18"

run <<= run in Android

install <<= install in Android

project/build.properties
sbt.version=0.13.0

project/plugin.sbt
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.0.6")


sbt起動
sbt
sbtのコンソールが起動し、自動的に依存ファイルがインストールされてscalaが使えるようになる。

とりあえずJavaのままだけどビルドする。sbtのコンソール内で
compile
が通ればok。


JavaをScalaに書きなおす

MainActivity.javaをMainActivity.scalaにリネームし、書き直す。
package org.shokai.scalatestapp;

import android.app.Activity;
import android.os.Bundle;

class MainActivity extends Activity{
override def onCreate(savedInstanceState:Bundle){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

sbtコンソールでビルドする。
android:package-debug
同じ名前の.javaと.scalaが混在しているとビルドが通らないが、別名なら混在していていい。


adbで実機にインストールする
adb install -r bin/ScalaTestApp-debug.apk
scalaでアプリが書けましたね。



大文字ひらがなを小文字ひらがなに変換するアプリを作る

レイアウトのXMLとScalaで書きなおしたMainActivityを編集する。


res/layout/main.xml にボタンとEditTextを追加
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText
android:id="@+id/editTextSource"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textShortMessage" />
<Button
android:id="@+id/btnRun"
android:text="小文字に変換"
android:layout_height="wrap_content"
android:layout_width="match_parent" />
<TextView
android:id="@+id/textViewResult"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="result here" />
</LinearLayout>


src/org/shokai/scalatestapp/MainActivity.scala
package org.shokai.scalatestapp;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Button;
import android.view.View;
import android.view.View.OnClickListener;

class MainActivity extends Activity{

var editTextSource:EditText = _
var textViewResult:TextView = _
var btnRun:Button = _

override def onCreate(savedInstanceState:Bundle){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

editTextSource = findViewById(R.id.editTextSource).asInstanceOf[EditText]
textViewResult = findViewById(R.id.textViewResult).asInstanceOf[TextView]
btnRun = findViewById(R.id.btnRun).asInstanceOf[Button]

btnRun.setOnClickListener( new OnClickListener(){
override def onClick(v:View){
val source = editTextSource.getText().toString()
trace(s"source: $source")
val result = kana_downcase(source)
trace(s"result: $result")
textViewResult.setText(result)
}
})
}

def kana_downcase(str:String):String = {
val chars = scala.collection.immutable.Map[String,String](
"あ" -> "ぁ",
"い" -> "ぃ",
"う" -> "ぅ",
"え" -> "ぇ",
"お" -> "ぉ",
"つ" -> "っ",
"よ" -> "ょ",
"わ" -> "ゎ")

return str.split("").
map(c =>
if(chars.contains(c)) chars(c) else c
).mkString
}

def trace(message:String){
Log.v("ScalaTestApp", message)
}
}
scalaなので、変換表を気軽にMapで作って1つずつチェックして置換して結合とか書けるし、
いちいちtoString()しないで s"$変数名" で変数展開して文字列に埋め込んだりできて便利だった。

ビルドするとこうなる


.gitignore

Android SDKは中間ファイルをたくさん生成するので、Gitにコミットする時にディレクトリそのままaddするとdiffが酷い状態になる。
さらにscala android-sdk-pluginも中間ファイルを生成する。
必要ないファイルはignoreしておくべき。

.gitignore
.DS_Store
*.log
*~
*#*
*.class
.classpath
.project
bin
build.xml
local.properties
proguard.cfg
.settings
target
project/target
tmp
local.propertiesなどもcommitしないようにしているので、リポジトリをcloneしなおして来た直後は
android update project --path `pwd`
を実行して、それぞれの環境に合わせたAndroid SDKのパスを生成しなおすようにしている。
こういうビルド作法はREADMEに書いておくといい

0

Scalaをちょっと勉強した

Scalaを勉強した結果、今日のNode.js初級入門勉強会でcoffee-scriptが書けるようになってた。coffee気持ち悪いと思ってたけど苦手克服できてよかった。


Java/JRuby/Scala

ScalaはJava。JRubyはRuby。
ScalaはJavaのシンタックスをシンプルに直して、Java使ってて不満な文字列、正規表現、配列やMap、無名関数や高階関数などの機能を追加した、より良いJavaという印象。
JRubyはRubyがJVM上で動いていて、ついでにJavaの機能も使える言語だと思う。

JRubyと比べると、JRubyよりもJavaに寄っている気がする。Javaのライブラリをスクリプト言語から使いたい時は今後はscala使おうと思う。

Scalaは型宣言をしなければならないのが良い。JRubyの方がRubyに寄っていて、スクリプト言語としての使い勝手はRubyの方が良いと思うけど、型がRubyの型なのかJavaの型なのかよくわからなくなって辛かった。
もちろんJVM上でRailsアプリを書くみたいな、Javaの機能は使わないでRubyだけでやるならJRubyは問題ないと思う。

スクリプト言語としての書き心地はRubyの方が好みだけど、スクリプト言語からJavaを使いたいならScalaの方がいい。


インストール

brew install scala
scala -version

scalaコマンドでインタラクティブシェルが起動して、適当に書いてtabを押せばメソッド名一覧とかでてくる。
おかげで特に本とか読まずにいきなりコード書ける。


練習問題

研究室のwikiにスクリプト言語の便利な機能を使わざるを得ないRubyの初級問題集がある。


“abracadabra” などに “a” がいくつあるか数える

正規表現を使う。String.rでRegexpになる。メソッドチェインでつないでいける。
: の後ろが型。


「また大阪か」を「ま た 大 阪 か」に変換する

いったん文字列を配列に分解してから結合し直す
return書かなくてもいい。


フィボナッチ数列をどんどん生成する

無名関数をコールバック関数として登録するようにした。
defじゃなくてvalに関数オブジェクトを保存するようにしてみた。valは定数でvarは再代入可能な変数。
普通のArrayは中身をいじれないので、mutable.ListBufferかmutalbe.ArrayBufferを使う。
配列の添字が[]ではなく()だった。Unitはvoidの事。
BigIntを使ったら9223372036854775807桁まで数値計算できた。


最大公約数を求める

可変長引数を使って、引数を何個でも取れるようにした。
<-はrange。大きい数から試行したくてRange.reverseしてみたらちゃんと動いてくれてうれしかった。
Arrayにminやmaxといったメソッドがあって楽だった。
map,filter,forallなんかも良い。


fizzbuzz

scalaは型が固いのがいいのだけど、文字列と数値どちらも渡せる関数を作りたい時もある。
Any型を使えばどんな型でも渡せる。


「ゎたしはっらぃょ」のように、小さい平仮名に変換しろ

argvで実行時引数が取れる。Mapも機能が豊富で使いやすかった。
三項演算子はないけどif elseで同じ事ができる。


素因数分解

末尾再帰の結果をlistで巻き戻して返す。
Scalaコレクションメソッドメモ(Hishidama's Scala collection method Memo)を見たら、四則演算子でlist同士を連結したり色々と操作する機能が充実してた。