/*
SpheroDeviceService.java
Copyright (c) 2014 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.sphero;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.orbotix.ConvenienceRobot;
import com.orbotix.common.Robot;
import org.deviceconnect.android.deviceplugin.sphero.SpheroManager.DeviceDiscoveryListener;
import org.deviceconnect.android.deviceplugin.sphero.data.DeviceInfo;
import org.deviceconnect.android.deviceplugin.sphero.data.SpheroParcelable;
import org.deviceconnect.android.deviceplugin.sphero.profile.SpheroProfile;
import org.deviceconnect.android.deviceplugin.sphero.profile.SpheroServiceDiscoveryProfile;
import org.deviceconnect.android.deviceplugin.sphero.profile.SpheroSystemProfile;
import org.deviceconnect.android.deviceplugin.sphero.service.SpheroService;
import org.deviceconnect.android.deviceplugin.sphero.setting.SettingActivity;
import org.deviceconnect.android.event.Event;
import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.event.cache.MemoryCacheController;
import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.profile.SystemProfile;
import org.deviceconnect.android.service.DConnectService;
import org.deviceconnect.android.service.DConnectServiceListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Spheroデバイスプラグイン.
* @author NTT DOCOMO, INC.
*/
public class SpheroDeviceService extends DConnectMessageService implements DeviceDiscoveryListener,
DConnectServiceListener {
/** TAG. */
private static final String TAG = SpheroDeviceService.class.getSimpleName();
/** Action NameSpace. */
private static final String ACTION_NAMESPACE = SpheroDeviceService.class.getPackage().getName() + ".action";
/**
* 検知開始アクション.
*/
public static final String ACTION_START_DISCOVERY = ACTION_NAMESPACE + ".START_DISCOVERY";
/**
* 検知終了アクション.
*/
public static final String ACTION_STOP_DISCOVERY = ACTION_NAMESPACE + ".STOP_DISCOVERY";
/**
* 接続アクション.
*/
public static final String ACTION_CONNECT = ACTION_NAMESPACE + ".CONNECT";
/**
* 接続解除アクション.
*/
public static final String ACTION_DISCONNECT = ACTION_NAMESPACE + ".DISCONNECT";
/**
* 接続済みデバイス取得アクション.
*/
public static final String ACTION_GET_CONNECTED = ACTION_NAMESPACE + ".GET_CONNECTED";
/**
* 見つかっているが、接続されていないデバイス取得アクション.
*/
public static final String ACTION_GET_FOUND = ACTION_NAMESPACE + ".GET_FOUND";
/**
* デバイス削除アクション.
*/
public static final String ACTION_DELETE_DEVICE = ACTION_NAMESPACE + ".DELETE_DEVICE";
/**
* Extraキー : {@value} .
*/
public static final String EXTRA_ID = "id";
/**
* レシーバー.
*/
private BroadcastReceiver mReceiver;
/**
* Received a event that Bluetooth has been changed.
*/
private final BroadcastReceiver mSensorReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
Collection<DeviceInfo> devices = SpheroManager.INSTANCE.getConnectedDevices();
for (DeviceInfo info : devices) {
DConnectService service = getServiceProvider().getService(info.getDevice().getRobot().getIdentifier());
if (service != null) {
if (state == BluetoothAdapter.STATE_ON) {
connectingSphero(service.getId());
} else if (state == BluetoothAdapter.STATE_OFF) {
service.setOnline(false);
}
}
}
}
}
};
/**
* Instance of handler.
*/
private final Handler mHandler = new Handler();
@Override
public void onCreate() {
super.onCreate();
EventManager.INSTANCE.setController(new MemoryCacheController());
SpheroManager.INSTANCE.setService(this);
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CONNECT);
filter.addAction(ACTION_DISCONNECT);
filter.addAction(ACTION_GET_CONNECTED);
filter.addAction(ACTION_START_DISCOVERY);
filter.addAction(ACTION_STOP_DISCOVERY);
filter.addAction(ACTION_GET_FOUND);
filter.addAction(ACTION_DELETE_DEVICE);
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
onStartCommand(intent, 0, 0);
}
};
lbm.registerReceiver(mReceiver, filter);
registerBluetoothFilter();
addProfile(new SpheroServiceDiscoveryProfile(getServiceProvider()));
getServiceProvider().addServiceListener(this);
}
@Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (intent != null) {
String action = intent.getAction();
if (action.equals(ACTION_START_DISCOVERY)) {
SpheroManager.INSTANCE.setDiscoveryListener(this);
SpheroManager.INSTANCE.startDiscovery(this);
} else if (action.equals(ACTION_STOP_DISCOVERY)) {
SpheroManager.INSTANCE.setDiscoveryListener(null);
SpheroManager.INSTANCE.stopDiscovery();
} else if (action.equals(ACTION_CONNECT)) {
final String id = intent.getStringExtra(EXTRA_ID);
connectingSphero(id);
} else if (action.equals(ACTION_DISCONNECT)) {
String id = intent.getStringExtra(EXTRA_ID);
disconnectingSphero(id);
} else if (action.equals(ACTION_GET_CONNECTED)) {
Collection<DeviceInfo> devices = SpheroManager.INSTANCE.getConnectedDevices();
Intent res = new Intent();
res.setAction(SettingActivity.ACTION_ADD_CONNECTED_DEVICE);
ArrayList<SpheroParcelable> devs = new ArrayList<SpheroParcelable>();
for (DeviceInfo info : devices) {
DConnectService service = getServiceProvider().getService(info.getDevice().getRobot().getIdentifier());
if (service != null) {
service.setOnline(true);
}
devs.add(new SpheroParcelable(info.getDevice().getRobot().getIdentifier(),
info.getDevice().getRobot().getName(),
SpheroParcelable.SpheroState.Remember));
}
res.putParcelableArrayListExtra(SettingActivity.EXTRA_DEVICES, devs);
LocalBroadcastManager.getInstance(this).sendBroadcast(res);
} else if (action.equals(ACTION_GET_FOUND)) {
List<Robot> devices = SpheroManager.INSTANCE.getFoundDevices();
Intent res = new Intent();
res.setAction(SettingActivity.ACTION_ADD_FOUNDED_DEVICE);
ArrayList<SpheroParcelable> devs = new ArrayList<SpheroParcelable>();
for (Robot device : devices) {
DConnectService service = getServiceProvider().getService(device.getIdentifier());
DeviceInfo info = SpheroManager.INSTANCE.getDevice(device.getIdentifier());
if (service == null) {
if (info != null) {
service = new SpheroService(info);
getServiceProvider().addService(service);
}
}
if ((info != null
&& info.getDevice().getRobot().getIdentifier().equals(device.getIdentifier())
&& device.isOnline())
|| (info == null && device.isOnline())) {
devs.add(new SpheroParcelable(device.getIdentifier(),
device.getName(),
SpheroParcelable.SpheroState.Connected));
} else if (info == null && device.isOnline()) {
devs.add(new SpheroParcelable(device.getIdentifier(),
device.getName(),
SpheroParcelable.SpheroState.Connected));
} else {
devs.add(new SpheroParcelable(device.getIdentifier(),
device.getName(),
SpheroParcelable.SpheroState.Delete));
}
}
res.putParcelableArrayListExtra(SettingActivity.EXTRA_DEVICES, devs);
LocalBroadcastManager.getInstance(this).sendBroadcast(res);
} else if (action.equals(ACTION_DELETE_DEVICE)) {
String id = intent.getStringExtra(EXTRA_ID);
Robot device = SpheroManager.INSTANCE.getNotConnectedDevice(id);
DConnectService service = getServiceProvider().getService(id);
if (service != null) {
SpheroManager.INSTANCE.removeNotConnectedDevice(id);
getServiceProvider().removeService(id);
sendDevice(SettingActivity.ACTION_DELETED, new ConvenienceRobot(device), SpheroParcelable.SpheroState.Delete);
}
} else {
return super.onStartCommand(intent, flags, startId);
}
}
return START_STICKY;
}
private void disconnectingSphero(String id) {
DeviceInfo info = SpheroManager.INSTANCE.getDevice(id);
DConnectService service = getServiceProvider().getService(id);
if (service != null) {
service.setOnline(false);
}
SpheroManager.INSTANCE.disconnect(id);
if (info != null) {
sendDevice(SettingActivity.ACTION_DISCONNECTED, info.getDevice(), SpheroParcelable.SpheroState.Disconnected);
}
}
/**
* Spheroと接続処理を行う.
* @param id SpheroのID
*/
private void connectingSphero(final String id) {
new Thread(new Runnable() {
@Override
public void run() {
if (SpheroManager.INSTANCE.connect(id)) {
if (BuildConfig.DEBUG) {
Log.d("TEST", "************ connected **********");
}
DeviceInfo info = SpheroManager.INSTANCE.getDevice(id);
ConvenienceRobot device;
if (info == null) {
device = null;
} else {
device = info.getDevice();
}
if (device != null) {
DConnectService service = getServiceProvider().getService(info.getDevice().getRobot().getIdentifier());
if (service == null) {
service = new SpheroService(info);
getServiceProvider().addService(service);
}
service.setOnline(true);
sendDevice(SettingActivity.ACTION_CONNECTED, device, SpheroParcelable.SpheroState.Connected);
} else {
sendDevice(SettingActivity.ACTION_CONNECTED, null, SpheroParcelable.SpheroState.Error);
}
} else {
if (BuildConfig.DEBUG) {
Log.d("TEST", "************ failed to connect **********");
}
sendDevice(SettingActivity.ACTION_CONNECTED, null, SpheroParcelable.SpheroState.Error);
}
}
}).start();
}
@Override
protected SystemProfile getSystemProfile() {
return new SpheroSystemProfile();
}
@Override
public void onDestroy() {
super.onDestroy();
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
lbm.unregisterReceiver(mReceiver);
unregisterBluetoothFilter();
getServiceProvider().removeServiceListener(this);
SpheroManager.INSTANCE.shutdown();
}
@Override
public void onDeviceFound(ConvenienceRobot sphero) {
sendDevice(SettingActivity.ACTION_ADD_DEVICE, sphero, SpheroParcelable.SpheroState.Remember);
}
@Override
public void onDeviceLost(ConvenienceRobot sphero) {
disconnectingSphero(sphero.getRobot().getIdentifier());
sendDevice(SettingActivity.ACTION_REMOVE_DEVICE, sphero, SpheroParcelable.SpheroState.Delete);
}
@Override
public void onDeviceLostAll() {
sendDevice(SettingActivity.ACTION_REMOVE_DEVICE_ALL, null, SpheroParcelable.SpheroState.Delete);
}
@Override
protected void onManagerUninstalled() {
// Managerアンインストール検知時の処理。
if (BuildConfig.DEBUG) {
Log.d("TEST","Plug-in : onManagerUninstalled");
}
EventManager.INSTANCE.removeAll();
resetSpheroEvents();
}
@Override
protected void onManagerTerminated() {
// Manager正常終了通知受信時の処理。
if (BuildConfig.DEBUG) {
Log.d("TEST", "Plug-in : onManagerTerminated");
}
EventManager.INSTANCE.removeAll();
resetSpheroEvents();
}
@Override
protected void onManagerEventTransmitDisconnected(String sessionKey) {
// ManagerのEvent送信経路切断通知受信時の処理。
if (BuildConfig.DEBUG) {
Log.d("TEST", "Plug-in : onManagerEventTransmitDisconnected");
}
if (sessionKey != null) {
EventManager.INSTANCE.removeEvents(sessionKey);
} else {
EventManager.INSTANCE.removeAll();
}
resetSpheroEvents();
}
@Override
public void onServiceAdded(DConnectService service) {
if (BuildConfig.DEBUG) {
Log.d("TEST", "onServiceAdded: " + service.getName());
}
}
@Override
public void onServiceRemoved(final DConnectService service) {
if (BuildConfig.DEBUG) {
Log.d("TEST", "onServiceRemoved: " + service.getName());
}
SpheroManager.INSTANCE.disconnect(service.getId());
SpheroManager.INSTANCE.removeNotConnectedDevice(service.getId());
EventManager.INSTANCE.removeAll();
resetSpheroEvents();
}
@Override
public void onStatusChange(DConnectService service) {
if (BuildConfig.DEBUG) {
Log.d("TEST", "onStatusChange: " + service.getName());
}
}
@Override
protected void onDevicePluginReset() {
// Device Plug-inへのReset要求受信時の処理。
if (BuildConfig.DEBUG) {
Log.d("TEST", "Plug-in : onDevicePluginReset");
}
EventManager.INSTANCE.removeAll();
resetSpheroEvents();
}
/**
* Spheroが現在実行中のイベントを停止する.
*/
private void resetSpheroEvents() {
Collection<DeviceInfo> devices = SpheroManager.INSTANCE.getConnectedDevices();
for (DeviceInfo info : devices) {
if (!SpheroManager.INSTANCE.hasSensorEvent(info)) {
SpheroManager.INSTANCE.stopSensor(info);
}
List<Event> events = EventManager.INSTANCE.getEventList(
info.getDevice().getRobot().getIdentifier(), SpheroProfile.PROFILE_NAME,
SpheroProfile.INTER_COLLISION, SpheroProfile.ATTR_ON_COLLISION);
if (events.size() == 0) {
SpheroManager.INSTANCE.stopCollision(info);
}
}
}
/**
* デバイスの情報を送る.
*
* @param action アクション
* @param sphero デバイス情報
*/
private void sendDevice(final String action, final ConvenienceRobot sphero, final SpheroParcelable.SpheroState state) {
Intent res = new Intent();
res.setAction(action);
SpheroParcelable s = null;
if (sphero != null) {
s = new SpheroParcelable(sphero.getRobot().getIdentifier(),
sphero.getRobot().getName(),
state);
}
res.putExtra(SettingActivity.EXTRA_DEVICE, s);
LocalBroadcastManager.getInstance(this).sendBroadcast(res);
}
/**
* Bluetoothイベントの登録.
*/
private void registerBluetoothFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mSensorReceiver, filter, null, mHandler);
}
/**
* Bluetoothイベントの解除.
*/
private void unregisterBluetoothFilter() {
unregisterReceiver(mSensorReceiver);
}
}