今回はBeacappを使ったAndroidアプリ開発について、ご紹介します。
iOS編はこちら。
環境準備
今回の開発に必要なものは以下の通りです。
- ビーキャップのアカウント(3ヶ月無料)
- Android (OS 4.3以上)
- Beacon端末または、iPhone(発信端末として使います)
- BeacappSDK for Android 1.2.1
- Mac (OS X EL Capitan)
- Android Studio 2.1.1
ビーキャップのアカウント作成
以下の記事を参考に、アカウント作成、イベント登録を実施します。
Beacappを使って簡単なアプリを作ってみる(準備編)
Androidアプリへの組み込み
アプリを組み込むプロジェクトを用意します。
既存のプロジェクトでも新規プロジェクトでも問題ありません。
今回は、新規のプロジェクトを作成します。
ちなみに、今回のAndroidサンプルはこちらに置いてあります。
Beacapp Android Sample - Github
プロジェクトの作成
MacからAndroid Studioを起動します。
[Start a new Android Studio project]を選択しましょう。
適当にアプリ名を入力します。
[Phone and Tablet] を選択し、[API Lebel] 4.3以上にします。
[Empty Activity] を選択します。
最後に適当にActivity名を入力します。
これで、プロジェクトの作成は完成です。
プロジェクトにBeacappSDKをインポートする
GithubからBeacappSDKをダウンロードします。
BeacappSDK for Android -Github
必要なのは、BeacappSDKforAndroidフォルダ内の.jarファイルだけです。
ダウンロードしたら、Androidプロジェクトにインポートしましょう。
表示を[Project]ビューに変更します。
その他の必要なライブラリをインポートする
[build.gradle]に必要なライブラリを記載します。
2種類ありますが、(Module: app)と記載のあるファイルに、情報を追記します。
必要なライブラリは、AWSSDK関連なので、以下のように記載します。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.amazonaws:aws-android-sdk-core:2.2.10'
compile 'com.amazonaws:aws-android-sdk-ddb:2.2.10'
compile 'com.amazonaws:aws-android-sdk-ddb-mapper:2.2.10'
compile 'com.amazonaws:aws-android-sdk-kinesis:2.2.10'
}
Gradleファイルを変更すると、右上に[Sync Now]と出てきますので、Syncしておきましよう。
これで、ライブラリ関連の準備はOKです。
JBCPサービスを呼び出す
Beacappは、バックグラウンドでもBeaconを検知するために、サービスを使用していますので、サービスをManifestに追加します。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Beacappを使用するために必要 -->
<service
android:name="com.beacapp.service.JBCPScanService"
android:exported="false"
android:process=":bleservice">
<intent-filter>
<action android:name="com.beacapp.BLEServiceAIDL"/>
<action android:name="com.beacapp.BLEServiceCalbackAIDL"/>
</intent-filter>
</service>
</application>
また、Bluetoothアクセス、GPSアクセス、インターネットアクセスを行いますので、パーミッションを記載する必要があります。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
Beacappを呼び出す
パッケージのインポート
まずは、呼び出すクラスで、必要なパッケージをインポートします。
import com.beacapp.FireEventListener;
import com.beacapp.JBCPException;
import com.beacapp.JBCPManager;
import com.beacapp.ShouldUpdateEventsListener;
import com.beacapp.UpdateEventsListener;
-
com.beacapp.FireEventListener
イベントの発火を受け取ります。 -
com.beacapp.JBCPException
BeacappがスローするExceptionです。 -
com.beacapp.JBCPManager
BeacappSDKのコア部分。 -
com.beacapp.ShouldUpdateEventsListener
イベントの更新を行うかどうかを返却します。 -
com.beacapp.UpdateEventsListener
イベント更新が完了したタイミングで呼び出されます。
このタイミングで、スキャンを開始するのが良いと思います。
アクティベーション処理
JBCPManager.getManager()を呼び出せば良いのですが、通信処理を行うため、別スレッドから呼び出す形にしておきます。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//通信が走るため、別スレッドでの処理にする
new Thread(new Runnable() {
@Override
public void run() {
jbcpManager = JBCPManager.getManager(MainActivity.this,
"BEACAPP_REQUEST_TOKEN",
"BEACAPP_SECRET_KEY",
null);
}
}).start();
}
各種リスナーの登録
リスナーをそれぞれ生成して、
// リスナーを生成
private UpdateEventsListener updateEventsListener = new UpdateEventsListener() {
@Override
public void onProgress(int i, int i1) {
}
@Override
public void onFinished(JBCPException e) {
}
};
// リスナーを生成
private ShouldUpdateEventsListener shouldUpdateEventsListener = new ShouldUpdateEventsListener() {
@Override
public boolean shouldUpdate(Map<String, Object> map) {
return true;
}
};
// リスナーを生成
private FireEventListener fireEventListener = new FireEventListener() {
@Override
public void fireEvent(JSONObject jsonObject) {
}
};
登録します。
//通信が走るため、別スレッドでの処理にする
new Thread(new Runnable() {
@Override
public void run() {
jbcpManager = JBCPManager.getManager(MainActivity.this,
"BEACAPP_REQUEST_TOKEN",
"BEACAPP_SECRET_KEY",
null);
if (jbcpManager == null){
return;
}
// リスナーを登録
jbcpManager.setUpdateEventsListener(updateEventsListener);
jbcpManager.setShouldUpdateEventsListener(shouldUpdateEventsListener);
jbcpManager.setFireEventListener(fireEventListener);
}
}).start();
最後にイベントの更新を要求します。
// イベントを更新する
jbcpManager.startUpdateEvents();
リスナー内に処理を書く
イベント更新完了のタイミングで、スキャン開始
// リスナーを生成
private UpdateEventsListener updateEventsListener = new UpdateEventsListener() {
@Override
public void onProgress(int i, int i1) {
}
@Override
public void onFinished(JBCPException e) {
if (e != null)
{
jbcpManager.startScan();
}
}
};
とりあえずイベントを検知したら、ログを出力するだけの処理
// リスナーを生成
private FireEventListener fireEventListener = new FireEventListener() {
@Override
public void fireEvent(JSONObject jsonObject) {
JSONObject action_data = jsonObject.optJSONObject("action_data");
String action = action_data.optString("action");
// URLの場合
if(action.equals("jbcp_open_url"))
{
Log.d("DEBUG",action_data.optString("url"));
}
//画像の場合
else if(action.equals("jbcp_open_image"))
{
Log.d("DEBUG",action_data.optString("image"));
}
//カスタムの場合
else if(action.equals("jbcp_custom_key_value"))
{
Log.d("DEBUG",action_data.optString("key_values"));
}
//テキストの場合
else if(action.equals("jbcp_open_text"))
{
Log.d("DEBUG",action_data.optString("text"));
}
}
};
これでおしまい、ではなく。。。
Android6だとパーミッションに記載があっても動作しません。
なので、明示的にアラートを表示して認証をしないといけません。
最終的に、以下のようになりました。
package jp.co.jmas.beacappandroidsample;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.beacapp.FireEventListener;
import com.beacapp.JBCPException;
import com.beacapp.JBCPManager;
import com.beacapp.ShouldUpdateEventsListener;
import com.beacapp.UpdateEventsListener;
import org.json.JSONObject;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private JBCPManager jbcpManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String permissions[] = {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
};
boolean needPermission = false;
for (String permission : permissions) {
if (checkSelfPermission(permission) ==
PackageManager.PERMISSION_GRANTED) {
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
permission)) {
needPermission = true;
} else {
needPermission = true;
}
}
}
if (needPermission) {
showExplanationDialog(permissions, 0);
} else {
activate();
}
}
}
private void activate()
{
//通信が走るため、別スレッドでの処理にする
new Thread(new Runnable() {
@Override
public void run() {
try {
jbcpManager = JBCPManager.getManager(MainActivity.this,
"アクティベーションキー",
"シークレットキー",
null);
} catch (JBCPException e) {
return;
}
if (jbcpManager == null){
return;
}
// リスナーを登録
jbcpManager.setUpdateEventsListener(updateEventsListener);
jbcpManager.setShouldUpdateEventsListener(shouldUpdateEventsListener);
jbcpManager.setFireEventListener(fireEventListener);
// イベントを更新する
jbcpManager.startUpdateEvents();
}
}).start();
}
private void showExplanationDialog(final String permissions[], final int requestCode) {
ActivityCompat.requestPermissions(MainActivity.this,
permissions,requestCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (0 == requestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
activate();
}
}
}
// リスナーを生成
private UpdateEventsListener updateEventsListener = new UpdateEventsListener() {
@Override
public void onProgress(int i, int i1) {
//何もしない
}
@Override
public void onFinished(JBCPException e) {
if (e != null)
{
jbcpManager.startScan();
}
}
};
// リスナーを生成
private ShouldUpdateEventsListener shouldUpdateEventsListener = new ShouldUpdateEventsListener() {
@Override
public boolean shouldUpdate(Map<String, Object> map) {
return true;
}
};
// リスナーを生成
private FireEventListener fireEventListener = new FireEventListener() {
@Override
public void fireEvent(JSONObject jsonObject) {
JSONObject action_data = jsonObject.optJSONObject("action_data");
String action = action_data.optString("action");
// URLの場合
if(action.equals("jbcp_open_url"))
{
Log.d("DEBUG",action_data.optString("url"));
}
//画像の場合
else if(action.equals("jbcp_open_image"))
{
Log.d("DEBUG",action_data.optString("image"));
}
//カスタムの場合
else if(action.equals("jbcp_custom_key_value"))
{
Log.d("DEBUG",action_data.optString("key_values"));
}
//テキストの場合
else if(action.equals("jbcp_open_text"))
{
Log.d("DEBUG",action_data.optString("text"));
}
}
};
}
これで、実機ビルドすれば、動くと思います。
実機での確認の際には、Beacon発信アプリを用意してますので使ってみてください。
Beacheck on the App Store - iTunes - Apple
よくあるエラー
アクティベーションに失敗する!
アクティベーション時に発生するよくあるエラーに、以下のものがあります。
Error Domain=BeacappDomain Code=1000 "ネットワークエラー(401)"
これが発生した場合は、以下のケースが想定されます。
・PackageNameが間違っている(AndroidManifest内に記載)
・アクティベーションキーが間違っている
・シークレットキーが間違っている
CMSに登録した内容とあっているか、確認してみてください。
Beaconを検知しない
デバッグ用として、BeaconEventListnerクラスを用意しています。
SCAN_MODEを2にして、リスナーを登録すると検知できます。
※あくまでもテスト用なので、リリース時には使用しないでください。
// リスナーを生成
public BeaconEventListener beaconEventListener = new BeaconEventListener() {
@Override
public boolean targetBeaconDetected(BeaconEvent beaconEvent) {
// CMSで検知対象に登録されているBeacon
return false;
}
@Override
public boolean nonTargetBeaconDetected(BeaconEvent beaconEvent) {
// CMSで検知対象になっていないBeacon
return false;
}
};
何も反応しない
Android,Beacon端末の設定を確認してください。
・位置情報はONになっているか
・位置情報は許可されているか
・Bluetoothは許可されているか
・端末の故障などで、電波が出ていない
nonTargetBeaconDetectedだけ反応している
・Beacappに登録したBeaconのUUIDに誤りがある
targetBeaconDetectedが反応する
設定は正しくできています。
端末から距離を離れてみたり、動いてみたり、少し待ってみてください。
その他
その他、お困りごとがあれば、お気軽にお問い合わせください。
Beacapp サポートチーム
今後もBeacappを使ったアプリ開発について、ご紹介させていただきます。