Android x Twilio #4 アプリで一般電話からの着信を受けてみよう

2013.05.14

はじめに

前回に引き続き今回も QuickStart を進めてみたいと思います。前回はアプリから一般電話に電話をかけるサンプルを作りましたが、今回はその逆のアプリで一般電話からの着信を受けるサンプルを作ってみたいと思います。今回もサーバーサイドは Ruby on Rails で実装します。また、前回までのソースに追加で実装するので、本記事から始めたいかたも前回までの手順を踏んでいる必要があります。
ちなみに電話番号を購入しなければいけないので、トライアルアカウントのかたは決済登録しておく必要があります。ちなみに電話番号ひとつにつき ¥490 かかります。詳しい料金はこちらを参照してください。

Twilio アプリにクライアント名を登録できるようにする

まず CapabilityToken を生成する時点でクライアントを特定しておく必要があるので、クライアント名を登録できるように API を修正します。Twilio::Util::Capabilityallow_client_incoming にパラメータで渡された文字列をセットするようにします。

token_controller.rb

# coding:utf-8
class TokenController < ApplicationController
  # GET "/tokens"
  def index
    # アカウントのSIDと認証トークン
    account_sid = 'アカウントのSID'
    auth_token = 'アカウントの認証トークン'
    # set up
    capability = Twilio::Util::Capability.new account_sid, auth_token
    # TwilioアプリのSID
    capability.allow_client_outgoing 'TwilioアプリのSID'
    # クライアント名
    capability.allow_client_incoming params[:client]
    # Capability Tokenの生成
    @token = capability.generate
    render :text => @token
  end
end

これで http://hoge/token?client=xxxx のような感じでクライアント名を受け取れる API になりました。
次にクライアントアプリの MonkeyPhone.onInitialized() メソッド内でリクエストしている URL を上記の形式に修正します。

@Override
public void onInitialized()
{
    Log.d(TAG, "Twilio SDK is ready");
    try {
        // パラメータを付ける
        String request = "http://hoge/token?client=classmethod";
        String capabilityToken = HttpHelper.httpGet(request);
        device = Twilio.createDevice(capabilityToken, null /* DeviceListener */);
    } catch (Exception e) {
        Log.e(TAG, "Failed to obtain capability token: " + e.getLocalizedMessage());
    }
}

これでクライアントアプリから CapabilityToken を取得するときにユーザ任意のクライアント名を登録できるようになりました。

Twilio 電話番号を登録して設定する

次に Twilio 電話番号を登録しましょう。ここで登録した電話番号がアプリに対して発信する電話番号になります。まずは以下にアクセスしましょう。

https://jp.twilio.com/user/account/phone-numbers/incoming

「電話番号を購入」をクリックします。

twilio_receive01

「検索」をクリックすると購入できる電話番号が一覧で出てくるので、好きな電話番号の「購入」ボタンをクリックして、購入処理を表示される手順通りに進めて完了です。

twilio_receive02

購入が完了したら電話番号と Twilio アプリを関連付けましょう。右上のコンボボックスから Application を選択し、アプリケーションにこれまで作っていたアプリケーション名を選択して更新します。

twilio_receive03

これで電話番号の設定は完了です!次にアプリケーションの設定を開き Voice Request URL を設定しましょう。Voice Request URL は、いま購入した電話番号に発信したときにリクエストされる URL です。ここに以下の TwiML の URL を設定します。Rails アプリで静的なファイルを置きたい場合は public フォルダの中に保存しましょう。

call_client.xml

<Response>
    <Dial>
        <Client>classmethod</Client>
    </Dial>
</Response>

twilio_receive04

これでいま購入した電話番号に着信があると TwiML の中身が実行される(指定したクライアント名に電話をかける)ようになりました!あとはクライアントアプリの実装だけです。

着信を受けられるようにする

MonkeyPhone クラスに着信を受ける処理を追加する

次にクライアントアプリで着信を受けられるようにします。まずは MonkeyPhoneonInitialized() メソッドに着信を受けるための Intent を生成する処理を追加します。

@Override
public void onInitialized()
{
    Log.d(TAG, "Twilio SDK is ready");

    try {
        // パラメータを付ける
        String request = "http://hello-twilio.herokuapp.com/token?client=classmethod";
        String capabilityToken = HttpHelper.httpGet(request);
        mDevice = Twilio.createDevice(capabilityToken, null /* DeviceListener */);
        // 着信を受けるためのIntentを生成してセットする
        Intent intent = new Intent(mContext, HelloMonkeyActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        mDevice.setIncomingIntent(pendingIntent);
    } catch (Exception e) {
        Log.e(TAG, "Failed to obtain capability token: " + e.getLocalizedMessage());
    }
}

そして MonkeyPhone にもう一つ、以下のメソッドを追加しましょう。以下のメソッドは着信を許可し、通話する処理になります。着信があると Device のインスタンスと Connection のインスタンスが渡されるので、それらを引数にとって accept() メソッドで通話を許可しています。

public void handleIncomingConnection(Device inDevice, Connection inConnection) {
    Log.i(TAG, "着信");
    if (mConnection != null) {
        mConnection.disconnect();
    }
    mConnection = inConnection;
    mConnection.accept();
}

HelloMonkeyActivity クラスに着信を受ける処理を追加する

次に HelloMonkeyActivity クラスの修正です。ここでは先ほど MonkeyPhone に追加したメソッドを使って着信があったときに受けとる処理を実装していきます。なお、ここは QuickStart の実装通りでは動かない部分があったので若干修正しています。
ポイントは 32 〜 40 行目です。着信があると IntentHelloMonkeyActivity に投げられますが、その Intent から DeviceConnection インスタンスを受け取り、先ほど実装した MonkeyPhonehandleIncomingConnection() メソッドに渡しています。
ここの処理は QuickStart では onResume() のタイミングで行なっていましたが、何故か MonkeyPhone のインスタンスが null になってしまうので、もう一度取得し直す実装にしています。

public class HelloMonkeyActivity extends FragmentActivity implements View.OnClickListener, LoaderCallbacks<MonkeyPhone> {

    /** ... Activity methods ... */
    
    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 新しく受け取った Intent (Device, Connection が入ってる) をセットする
        setIntent(intent);
    }
    
    @Override
    public void onResume()
    {
        super.onResume();
        if (phone == null) {
        	// MonkeyPhone をロードする
        	getSupportLoaderManager().initLoader(0, null, this);
        }
    }
    
    /** ... LoaderCallbacks methods ... */
    
    @Override
    public void onLoaderReset(Loader<MonkeyPhone> loader) {
    }

    @Override
    public void onLoadFinished(Loader<MonkeyPhone> loader, MonkeyPhone monkeyPhone) {
    	getSupportLoaderManager().destroyLoader(0);
        phone = monkeyPhone;
        // Intent から Device, Connection を受け取る
        Intent intent = getIntent();
        Device device = intent.getParcelableExtra(Device.EXTRA_DEVICE);
        Connection connection = intent.getParcelableExtra(Device.EXTRA_CONNECTION);
        if (device != null && connection != null) {
            intent.removeExtra(Device.EXTRA_DEVICE);
            intent.removeExtra(Device.EXTRA_CONNECTION);
            phone.handleIncomingConnection(device, connection);
        }
    }

    @Override
    public Loader<MonkeyPhone> onCreateLoader(int id, Bundle bundle) {
        MonkeyPhoneLoader loader = new MonkeyPhoneLoader(getApplicationContext());
        loader.forceLoad();
        return loader;
    }
    
    /** ... other methods ... */
}

電話してみる

実装完了です。新しく取得した Twilio 電話番号に電話をかけてみましょう。自動で接続され通話できるはずです!

twilio_receive05

まとめ

前回と比べて少し複雑だったでしょうか。このサンプルでは自動的に接続していますが、着信があった時点で通話するか確認処理を挟むとより電話アプリっぽくなると思います。しかし、今回の HelloMonkeyActivityMonkeyPhone のインスタンスが null になってしまうのは何故でしょうか。。。アドバイスいただければ幸いです。
次回は QuickStart の最後、アプリから電話をかけてアプリで着信を受け取るサンプルに挑戦したいと思います!

参考