Vimから電話がかけられるなんて・・・素敵!

このエントリーはVim Advent Calendar2012の301日目の記事です。

Vimで電話?

スマホVimを入れてるVimmerなら、Vimから電話掛けれたらな〜って1度は思ったことありますよね?(オレだけ?)。この度そんな願望を叶えるためのunite-sourceをちょこっと書いてみたので紹介してみたいと思います。

電話帳の取得は?

もちろんAndroid端末は内部に連絡先のデータベースを持っており、そこから情報を取得できれば一番いいんですが、ターミナルエミュレータで動いてるAndroidネイティブなCUIアプリからそれをやるのは恐らく無理だと思われます。さてどうしたものかと考えて思いついたのは、スマホの「連絡先データのエクスポート」でSDカード上に出力されたファイルを使ったらどうか?というものでした。vcard形式のこのファイルのUTF-8エンコードされた部分をデコードしてリスト作成は出来るには出来るんですが、なんかあまりカッコよくない。んで最終的に辿り着いたのは、「ネットワーク経由でGoogle Contact APIを叩いて連絡先を取得する」方法です。オンラインじゃなきゃ使えませんが、これから電話しようって時にオフラインな状況下ってのはまず無いと思うので良しとします。

スマホの連絡先情報の保存

Android端末の連絡先データの保存先は、機種にもよりますが「1:キャリアのアカウント」、「2:本体」、「3:Googleアカウント」から選べる形になってると思います。この中で「3」を選ぶとデータがクラウド上で管理されるので携帯キャリアを変えたり機種変したりしても簡単にデータ移行できますし、PCのブラウザでGoogleにログインして編集したりといったことも可能です。今回Vimから扱うのもこのGoogleアカウントに紐づいた連絡先情報という事になります。
すでに連絡先を本体のみに保存してしまっている場合でもGoogleアカウントに同期させることは可能です。方法はググってみてください。

unite-sourceについて

Google Contact APIを叩く部分がcurlなのは仕方ないにしても、そこ以外はVimスクリプトだけで書きたかったんですが、精進不足でweb-apiが上手く使えなかったのでXMLのパースはrubyでやってます。動作にはAndroid上のターミナルアプリで端末Vimrubyが使える環境が必要で、1から作るには結構大変なんですが、拙作のrboxだとTermYYYYMMDD.apkのインストールだけで整います。IS01専用のMCVTってのもあります。
拙いコードで申し訳ないんですが、以下にのっけます。


let s:save_cpo = &cpo
set cpo&vim

" unite-source の設定を定義する
let s:unite_source = { 'name': 'denwa' ,
\ }
let s:denwa = []


" Google Contact API を叩いて連絡先を取得
function! s:get_contact_list()
let auth = vimproc#system("
\ curl -s https://www.google.com/accounts/ClientLogin
\ --cacert $RBOX/usr/share/curl/curl-ca-bundle.crt
\ -d Email=YOUR_EMAIL
\ -d Passwd=YOUR_PASSWD
\ -d accountType=GOOGLE
\ -d source=Google-Contact-Lister
\ -d Gdata-version=3.0
\ -d service=cp | grep ^Auth= | sed 's/^Auth=//' ")

let cmd = "curl -s https://www.google.com/m8/feeds/contacts/default/full?max-results=1000
\ --cacert $RBOX/usr/share/curl/curl-ca-bundle.crt
\ -H 'Content-Type: application/atom+xml'
\ -H 'GData-Version: 3.0'
\ -H 'Authorization: GoogleLogin auth=" . auth ."'"

let ret = vimproc#system(cmd)
return ret
endfunction


" XML の連絡先をパースしリストを作成
function! s:get_data()
let str = s:get_contact_list()
ruby << EOC
require 'rexml/document'
x='';y='';z=''
ruby_ret = VIM.evaluate('str')
doc = REXML::Document.new(ruby_ret)
doc.elements.each('feed/entry') do |element|
title = element.elements['title']
fname = element.elements["gd:name/gd:familyName"]
phoneno = element.elements['gd:phoneNumber']
if title.length > 0 && !phoneno.nil? then
x = title.text + " "
x = x + "(" + fname.attributes['yomi'] + ") " if !fname.nil? && !fname.attributes['yomi'].nil?
x = x + phoneno.text
y = "echo am start -a android.intent.action.DIAL tel:#{phoneno.text} | /system/bin/sh"
z = z + "[\"#{x}\",\"#{y}\"],"
end
end
z = "[#{z}]"
VIM.command(%Q[let s:denwa = #{z}])
EOC
endfunction


" unite.vim で表示される候補を返す
function! s:unite_source.gather_candidates(args, context)
if empty(s:denwa) | call s:get_data() | endif
return map(copy(s:denwa), '{
\ "word": v:val[0],
\ "source": "denwa",
\ "kind": "command",
\ "action__command": "call unite#util#system(''".v:val[1]."'')"
\ }')
endfunction


" unite.vim に source を登録
"call unite#define_source(s:unite_source)
"unlet s:unite_source
function! unite#sources#denwa#define()
return s:unite_source
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

YOUR_EMAIL,YOUR_PASSWDはご自分のものに置き換えてください。

使い方

◆ 上記のコードを"denwa.vim"という名前で ~/.vim/bundle/unite.vim/autoload/unite/sources に保存したら早速Vimを起動して"Unite denwa"してみましょう。






◆ 基本的には「氏名 (ふりがな) 電話番号」の形式で表示されます。(本当の私の連絡先を晒すわけには行かないので、下図はテストアカウント&テストデータによるものです。)



◆ 四天王で絞り込み、広目天さんに電話してみましょう。リスト上で[Enter]押下です。




◆ あとは[ダイアル]ボタンを押せば電話がかかります。




如何ですか?実用性はともかくw,Google Contact API & Unite & Androidインテントの組み合わせは結構面白いと思うんですけど。
応用としては、Uniteで選択した複数の連絡先のメールアドレスに、編集中のバッファを本文としてメールを送ることなんかも出来るんじゃないかなって考えたりしてます。
ではまたー。