https://linkingiot.com/developer/#developers より引用はじめに
NTTドコモ等の複数の国内企業が連携して「Linking」というプラットフォームが発表されました。
公開されたサイトを見てみると、どうやらLinkingとはIoT(Internet of Things)に関係するらしいフレーズが散りばめられています。
- すべてのモノが、ネットでつながる
- デバイス開発者も、アプリ開発者も、そしてユーザーも。
- Linkingプラットフォームが、つくるをつなぐ。
https://linkingiot.com/developer/#developers より引用はじめに
https://linkingiot.com/developer/#developers より引用はじめに
BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager != null) {
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
}
次にGATT Serverのインスタンスを取得します。(BluetoothManager#openGattServer)mGattServer = mBluetoothManager.openGattServer(this, new BLEServer());
class BLEServer extends BluetoothGattServerCallback {
//セントラルから読み込み要求が来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
}
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
}
}
private void setServices() {
//serviceUUIDを設定BluetoothGattService service = new BluetoothGattService(
UUID.fromString(Constants.SERVICE_UUID),
BluetoothGattService.SERVICE_TYPE_PRIMARY);
//characteristicUUIDを設定
BluetoothGattCharacteristic charRead = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_READ_UUID),
BluetoothGattCharacteristic.PROPERTY_READ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic charWrite = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_WRITE_UUID),
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
//characteristicUUIDをserviceUUIDにのせる
service.addCharacteristic(charRead);
service.addCharacteristic(charWrite);
//serviceUUIDをサーバーにのせる
mGattServer.addService(service);
}
次にアドバタイジング時の設定(AdvertiseSettings)とデータ(AdvertiseData)の設定を行います。//AdvertiseSettingsの設定
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
//AdvertiseDataの設定
private AdvertiseData buildAdvertiseData() {
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.addServiceUuid(ParcelUuid.fromString(Constants.SERVICE_UUID));
dataBuilder.setIncludeDeviceName(true);
return dataBuilder.build();
}
//Advertiseの開始
private void startAdvertising() {
setServices();
AdvertiseSettings settings = buildAdvertiseSettings();
AdvertiseData data = buildAdvertiseData();
mAdvertiseCallback = new SimpleAdvertiseCallback();
mBluetoothLeAdvertiser.startAdvertising(settings, data,mAdvertiseCallback);
}
//Advertiseの成功可否
private class SimpleAdvertiseCallback extends AdvertiseCallback {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(TAG, "Advertising failed");
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(TAG, "Advertising successfully started");
}
}
private void stopAdvertising() {
Log.d(TAG, "Service: Stopping Advertising");
if (mGattServer != null) {
mGattServer.clearServices();
mGattServer.close();
mGattServer = null;
}
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
mAdvertiseCallback = null;
}
}
以上で、Nexus 9がペリフェラルとして機能するようになります。public void readCharacteristic() {
BluetoothGattCharacteristic read = mBluetoothLeService.getCharacteristic(
GattAttributes.SERVICE_UUID,
GattAttributes.CHAR_READ_UUID);
mBluetoothGatt.readCharacteristic(read);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
}
//セントラルからReadRequestが来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
//セントラルに任意の文字を返信する
if (UUID.fromString(Constants. CHAR_READ_UUID).equals(characteristic.getUuid())) {
String response = "your message.";
byte value[] = response.getBytes();
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
}
public void writeCharacteristic() {
BluetoothGattCharacteristic write = getCharacteristic(
UUID.fromString(GattAttributes.SERVICE_UUID),
UUID.fromString(GattAttributes.CHAR_WRITE_UUID));
String message = "your message";
write.setValue(message);
mBluetoothGatt.writeCharacteristic(characteristic);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
Log.d(TAG, "onCharacteristicWriteRequest");
if (UUID.fromString(Constants.CHAR_WRITE_UUID).equals(characteristic.getUuid())) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
}
}
BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager != null) {
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
}
次にGATT Serverのインスタンスを取得します。(BluetoothManager#openGattServer)mGattServer = mBluetoothManager.openGattServer(this, new BLEServer());
class BLEServer extends BluetoothGattServerCallback {
//セントラルから読み込み要求が来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
}
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
}
}
private void setServices() {
//serviceUUIDを設定BluetoothGattService service = new BluetoothGattService(
UUID.fromString(Constants.SERVICE_UUID),
BluetoothGattService.SERVICE_TYPE_PRIMARY);
//characteristicUUIDを設定
BluetoothGattCharacteristic charRead = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_READ_UUID),
BluetoothGattCharacteristic.PROPERTY_READ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic charWrite = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_WRITE_UUID),
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
//characteristicUUIDをserviceUUIDにのせる
service.addCharacteristic(charRead);
service.addCharacteristic(charWrite);
//serviceUUIDをサーバーにのせる
mGattServer.addService(service);
}
次にアドバタイジング時の設定(AdvertiseSettings)とデータ(AdvertiseData)の設定を行います。//AdvertiseSettingsの設定
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
//AdvertiseDataの設定
private AdvertiseData buildAdvertiseData() {
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.addServiceUuid(ParcelUuid.fromString(Constants.SERVICE_UUID));
dataBuilder.setIncludeDeviceName(true);
return dataBuilder.build();
}
//Advertiseの開始
private void startAdvertising() {
setServices();
AdvertiseSettings settings = buildAdvertiseSettings();
AdvertiseData data = buildAdvertiseData();
mAdvertiseCallback = new SimpleAdvertiseCallback();
mBluetoothLeAdvertiser.startAdvertising(settings, data,mAdvertiseCallback);
}
//Advertiseの成功可否
private class SimpleAdvertiseCallback extends AdvertiseCallback {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(TAG, "Advertising failed");
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(TAG, "Advertising successfully started");
}
}
private void stopAdvertising() {
Log.d(TAG, "Service: Stopping Advertising");
if (mGattServer != null) {
mGattServer.clearServices();
mGattServer.close();
mGattServer = null;
}
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
mAdvertiseCallback = null;
}
}
以上で、Nexus 9がペリフェラルとして機能するようになります。public void readCharacteristic() {
BluetoothGattCharacteristic read = mBluetoothLeService.getCharacteristic(
GattAttributes.SERVICE_UUID,
GattAttributes.CHAR_READ_UUID);
mBluetoothGatt.readCharacteristic(read);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
}
//セントラルからReadRequestが来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
//セントラルに任意の文字を返信する
if (UUID.fromString(Constants. CHAR_READ_UUID).equals(characteristic.getUuid())) {
String response = "your message.";
byte value[] = response.getBytes();
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
}
public void writeCharacteristic() {
BluetoothGattCharacteristic write = getCharacteristic(
UUID.fromString(GattAttributes.SERVICE_UUID),
UUID.fromString(GattAttributes.CHAR_WRITE_UUID));
String message = "your message";
write.setValue(message);
mBluetoothGatt.writeCharacteristic(characteristic);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
Log.d(TAG, "onCharacteristicWriteRequest");
if (UUID.fromString(Constants.CHAR_WRITE_UUID).equals(characteristic.getUuid())) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
}
}
<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>※端末がBLEに対応しているかの確認
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE未対応端末です", Toast.LENGTH_SHORT).show();
finish();
}
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> または <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>しかし、それだけではBLE機能を使用することは出来ません。。
private boolean checkPermission() {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_LOCATION_STATE);
return false;
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (PERMISSIONS_REQUEST_LOCATION_STATE == requestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 許可された場合
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT).show();
} else {
// 不許可だった場合
Toast.makeText(this, "権限を拒否されました", Toast.LENGTH_SHORT).show();
finish();
}
}
}
final BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();次に、端末のBluetoothが有効になっているかの確認を行います。
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
// Device scan callback.
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, final ScanResult result) {
super.onScanResult(callbackType, result);
if (result != null && result.getDevice() != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
}
};
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);接続が確立されると、BluetoothGattCallback#onConnectionStateChangeが呼ばれます。
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
serviceList = gatt.getServices();
// サービスの内容を取得等処理を行う
// 取得したサービスからBLEデバイスの情報を取得する
}
<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>※端末がBLEに対応しているかの確認
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE未対応端末です", Toast.LENGTH_SHORT).show();
finish();
}
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> または <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>しかし、それだけではBLE機能を使用することは出来ません。。
private boolean checkPermission() {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_LOCATION_STATE);
return false;
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (PERMISSIONS_REQUEST_LOCATION_STATE == requestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 許可された場合
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT).show();
} else {
// 不許可だった場合
Toast.makeText(this, "権限を拒否されました", Toast.LENGTH_SHORT).show();
finish();
}
}
}
final BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();次に、端末のBluetoothが有効になっているかの確認を行います。
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
// Device scan callback.
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, final ScanResult result) {
super.onScanResult(callbackType, result);
if (result != null && result.getDevice() != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
}
};
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);接続が確立されると、BluetoothGattCallback#onConnectionStateChangeが呼ばれます。
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
serviceList = gatt.getServices();
// サービスの内容を取得等処理を行う
// 取得したサービスからBLEデバイスの情報を取得する
}
>ruby -v ruby 2.1.7p400 (2015-08-18 revision 51632) [x64-mingw32]
>gem install appium_lib
# encoding: utf-8
require "appium_lib"
desired_caps = {
caps: {
platformName: "Android",
deviceName: "Android Device",
app: "#{Dir.pwd}/selendroid-test-app.apk",
}
}
driver = Appium::Driver.new(desired_caps).start_driver
Appium.promote_appium_methods Object
button_element = find_element(:id, "io.selendroid.testapp:id/visibleButtonTest")
button_element.click
sleep 3
text_element = find_element(:id, "io.selendroid.testapp:id/visibleTextView")
displayed_text = text_element.text
button_element.click
sleep 3
puts displayed_text
driver.quit
>ruby sample.rb
>ruby -v ruby 2.1.7p400 (2015-08-18 revision 51632) [x64-mingw32]
>gem install appium_lib
# encoding: utf-8
require "appium_lib"
desired_caps = {
caps: {
platformName: "Android",
deviceName: "Android Device",
app: "#{Dir.pwd}/selendroid-test-app.apk",
}
}
driver = Appium::Driver.new(desired_caps).start_driver
Appium.promote_appium_methods Object
button_element = find_element(:id, "io.selendroid.testapp:id/visibleButtonTest")
button_element.click
sleep 3
text_element = find_element(:id, "io.selendroid.testapp:id/visibleTextView")
displayed_text = text_element.text
button_element.click
sleep 3
puts displayed_text
driver.quit
>ruby sample.rb
| HID(Human Interface Device Profile) | 入力機器を扱うためのプロファイル |
| HSP(Headset Profile) | ヘッドセットと通信するためのプロファイル |
| HFP(Hands-Free Profile) | ハンズフリー通話をするためのプロファイル |
| A2DP(Advanced Audio Distribution Profile) | 高音質のステレオ音声を伝送するためのプロファイル |
| AVRCP(Audio/Video Remote Control Profile) | AV機器のコントロール(再生、早送り等)を行うためのプロファル |
| FTP(File Transfer Profile) | ファイル転送プロファイル |
| ANP(Alert Notification Profile) | 電話やメールなどの着信を通知するプロファイル |
| BLP(Blood Pressure Profile) | 血圧計から血圧情報を伝送するためのプロファイル |
| CPP(Cycling Power Profile) | サイクリング時の力などを情報を伝送するためのプロファイル |
| CSCP(Cycling Speed and Cadence Profile) | サイクリング時のスピードや回転数などの情報を伝送するためのプロファイル |
| FMP(Find Me Profile) | 遠隔からアラームやバイブレーションを鳴動させ、装置の場所を調べるためのプロファイル |
| GLP(Glucose Profile) | 血糖値の情報を伝送するためのプロファイル |
| HOGP(HID over GATT Profile) | マウスやキーボードなどを接続するためのプロファイル |
| HTP(Health Thermometer Profile) | 体温計の情報を伝送するためのプロファイル |
| HRP(Heart Rate Profile) | 心拍計の情報を伝送するためのプロファイル |
| IPSP (Internet Protocol Support Profile) | IPv6/6LoWPAN経由でインターネットに接続するプロファイル |
| PASP(Phone Alert Status Profile) | 電話着信時の鳴動などを他の機器から止めるためのプロファイル |
| PXP(Proximity Profile) | 互いの機器間の距離をモニタリングするためのプロファイル |
| RSCP(Running Speed and Cadence Profile) | ランニング時のスピードや歩幅などの情報を伝送するためのプロファイル |
| ScPP(Scan Parameters Profile) | 装置のスキャンをするためのプロファイル |
| TIP(Time Profile) | 時刻情報を通知するためのプロフィール |
| HID(Human Interface Device Profile) | 入力機器を扱うためのプロファイル |
| HSP(Headset Profile) | ヘッドセットと通信するためのプロファイル |
| HFP(Hands-Free Profile) | ハンズフリー通話をするためのプロファイル |
| A2DP(Advanced Audio Distribution Profile) | 高音質のステレオ音声を伝送するためのプロファイル |
| AVRCP(Audio/Video Remote Control Profile) | AV機器のコントロール(再生、早送り等)を行うためのプロファル |
| FTP(File Transfer Profile) | ファイル転送プロファイル |
| ANP(Alert Notification Profile) | 電話やメールなどの着信を通知するプロファイル |
| BLP(Blood Pressure Profile) | 血圧計から血圧情報を伝送するためのプロファイル |
| CPP(Cycling Power Profile) | サイクリング時の力などを情報を伝送するためのプロファイル |
| CSCP(Cycling Speed and Cadence Profile) | サイクリング時のスピードや回転数などの情報を伝送するためのプロファイル |
| FMP(Find Me Profile) | 遠隔からアラームやバイブレーションを鳴動させ、装置の場所を調べるためのプロファイル |
| GLP(Glucose Profile) | 血糖値の情報を伝送するためのプロファイル |
| HOGP(HID over GATT Profile) | マウスやキーボードなどを接続するためのプロファイル |
| HTP(Health Thermometer Profile) | 体温計の情報を伝送するためのプロファイル |
| HRP(Heart Rate Profile) | 心拍計の情報を伝送するためのプロファイル |
| IPSP (Internet Protocol Support Profile) | IPv6/6LoWPAN経由でインターネットに接続するプロファイル |
| PASP(Phone Alert Status Profile) | 電話着信時の鳴動などを他の機器から止めるためのプロファイル |
| PXP(Proximity Profile) | 互いの機器間の距離をモニタリングするためのプロファイル |
| RSCP(Running Speed and Cadence Profile) | ランニング時のスピードや歩幅などの情報を伝送するためのプロファイル |
| ScPP(Scan Parameters Profile) | 装置のスキャンをするためのプロファイル |
| TIP(Time Profile) | 時刻情報を通知するためのプロフィール |
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.raw" />
CameraCharacteristics characteristics
= manager.getCameraCharacteristics(cameraId);
// We only use a camera that supports RAW in this sample.
if (!contains(characteristics.get(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES),
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
continue;
}
if (mRawImageReader == null || mRawImageReader.getAndRetain() == null) {
mRawImageReader = new RefCountedAutoCloseable<>(<
ImageReader.newInstance(largestRaw.getWidth(),
largestRaw.getHeight(), ImageFormat.RAW_SENSOR, /*maxImages*/ 5));
}
mRawImageReader.get().setOnImageAvailableListener(
mOnRawImageAvailableListener, mBackgroundHandler);
}
private void openCamera() {
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
manager.openCamera(cameraId, mStateCallback, backgroundHandler);
}
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
synchronized (mCameraStateLock) {
// Start the preview session if the TextureView has been set up already.
if (mPreviewSize != null && mTextureView.isAvailable()) {
createCameraPreviewSessionLocked();
}
}
}
private void createCameraPreviewSessionLocked() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface,
mJpegImageReader.get().getSurface(),
mRawImageReader.get().getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
synchronized (mCameraStateLock) {
setup3AControlsLocked(mPreviewRequestBuilder);
// Finally, we start displaying the camera preview.
cameraCaptureSession.setRepeatingRequest(
mPreviewRequestBuilder.build(),
mPreCaptureCallback, mBackgroundHandler);
mState = STATE_PREVIEW;
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void takePicture() {
// Replace the existing repeating request with one with updated 3A triggers.
mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback,
mBackgroundHandler);
}
private void captureStillPictureLocked() {
// Use the same AE and AF modes as the preview.
setup3AControlsLocked(captureBuilder);
// Create an ImageSaverBuilder in which to collect results, and add it to the queue
// of active requests.
ImageSaver.ImageSaverBuilder rawBuilder = new ImageSaver.ImageSaverBuilder(activity)
.setCharacteristics(mCharacteristics);
mRawResultQueue.put((int) request.getTag(), rawBuilder);
mCaptureSession.capture(request, mCaptureCallback, mBackgroundHandler);
}
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
synchronized (mCameraStateLock) {
rawBuilder = mRawResultQueue.get(requestId);
handleCompletionLocked(requestId, rawBuilder, mRawResultQueue);
if (rawBuilder != null) {
rawBuilder.setResult(result);
if (jpegBuilder != null) sb.append(", ");
sb.append("Saving RAW as: ");
sb.append(rawBuilder.getSaveLocation());
}
finishedCaptureLocked();
}
}
private void handleCompletionLocked(int requestId, ImageSaver.ImageSaverBuilder builder,
TreeMap<Integer, ImageSaver.ImageSaverBuilder> queue) {
ImageSaver saver = builder.buildIfComplete();
if (saver != null) {
queue.remove(requestId);
AsyncTask.THREAD_POOL_EXECUTOR.execute(saver);
}
}
switch (format) {
case ImageFormat.RAW_SENSOR: {
DngCreator dngCreator = new DngCreator(mCharacteristics, mCaptureResult);
FileOutputStream output = null;
try {
output = new FileOutputStream(mFile);
dngCreator.writeImage(output, mImage);
success = true;
} finally {
mImage.close();
closeOutput(output);
}
}
}
if (success) {
MediaScannerConnection.scanFile(mContext, new String[]{mFile.getPath()},
/*mimeTypes*/null, new MediaScannerConnection.MediaScannerConnectionClient() {
@Override
public void onScanCompleted(String path, Uri uri) {
Log.i(TAG, "Scanned " + path + ":");
Log.i(TAG, "-> uri=" + uri);
}
});
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.raw" />
CameraCharacteristics characteristics
= manager.getCameraCharacteristics(cameraId);
// We only use a camera that supports RAW in this sample.
if (!contains(characteristics.get(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES),
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
continue;
}
if (mRawImageReader == null || mRawImageReader.getAndRetain() == null) {
mRawImageReader = new RefCountedAutoCloseable<>(<
ImageReader.newInstance(largestRaw.getWidth(),
largestRaw.getHeight(), ImageFormat.RAW_SENSOR, /*maxImages*/ 5));
}
mRawImageReader.get().setOnImageAvailableListener(
mOnRawImageAvailableListener, mBackgroundHandler);
}
private void openCamera() {
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
manager.openCamera(cameraId, mStateCallback, backgroundHandler);
}
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
synchronized (mCameraStateLock) {
// Start the preview session if the TextureView has been set up already.
if (mPreviewSize != null && mTextureView.isAvailable()) {
createCameraPreviewSessionLocked();
}
}
}
private void createCameraPreviewSessionLocked() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface,
mJpegImageReader.get().getSurface(),
mRawImageReader.get().getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
synchronized (mCameraStateLock) {
setup3AControlsLocked(mPreviewRequestBuilder);
// Finally, we start displaying the camera preview.
cameraCaptureSession.setRepeatingRequest(
mPreviewRequestBuilder.build(),
mPreCaptureCallback, mBackgroundHandler);
mState = STATE_PREVIEW;
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void takePicture() {
// Replace the existing repeating request with one with updated 3A triggers.
mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback,
mBackgroundHandler);
}
private void captureStillPictureLocked() {
// Use the same AE and AF modes as the preview.
setup3AControlsLocked(captureBuilder);
// Create an ImageSaverBuilder in which to collect results, and add it to the queue
// of active requests.
ImageSaver.ImageSaverBuilder rawBuilder = new ImageSaver.ImageSaverBuilder(activity)
.setCharacteristics(mCharacteristics);
mRawResultQueue.put((int) request.getTag(), rawBuilder);
mCaptureSession.capture(request, mCaptureCallback, mBackgroundHandler);
}
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
synchronized (mCameraStateLock) {
rawBuilder = mRawResultQueue.get(requestId);
handleCompletionLocked(requestId, rawBuilder, mRawResultQueue);
if (rawBuilder != null) {
rawBuilder.setResult(result);
if (jpegBuilder != null) sb.append(", ");
sb.append("Saving RAW as: ");
sb.append(rawBuilder.getSaveLocation());
}
finishedCaptureLocked();
}
}
private void handleCompletionLocked(int requestId, ImageSaver.ImageSaverBuilder builder,
TreeMap<Integer, ImageSaver.ImageSaverBuilder> queue) {
ImageSaver saver = builder.buildIfComplete();
if (saver != null) {
queue.remove(requestId);
AsyncTask.THREAD_POOL_EXECUTOR.execute(saver);
}
}
switch (format) {
case ImageFormat.RAW_SENSOR: {
DngCreator dngCreator = new DngCreator(mCharacteristics, mCaptureResult);
FileOutputStream output = null;
try {
output = new FileOutputStream(mFile);
dngCreator.writeImage(output, mImage);
success = true;
} finally {
mImage.close();
closeOutput(output);
}
}
}
if (success) {
MediaScannerConnection.scanFile(mContext, new String[]{mFile.getPath()},
/*mimeTypes*/null, new MediaScannerConnection.MediaScannerConnectionClient() {
@Override
public void onScanCompleted(String path, Uri uri) {
Log.i(TAG, "Scanned " + path + ":");
Log.i(TAG, "-> uri=" + uri);
}
});
![]() |
| 画像1 [テキスト選択時(Android API Lv 22)] |
![]() |
| 画像2 [テキスト選択時(Android API Lv 23)] |
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.sample_text);
textView.setText(R.string.sample_text);
textView.setTextIsSelectable(true);
}
<TextView
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:text="@string/sample_text"
/>
![]() |
| 図3 [アクションモード] |
![]() |
| [実行の様子1] |
private class CustomItemClickedListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
ListView listView = (ListView)parent;
boolean hasChecked = false;
Rect rect = new Rect();
Rect sumRect = null;
int firstVisiblePosition = listView.getFirstVisiblePosition();
for (int i = 0; i < listView.getChildCount(); i++) {
TextView textView = (TextView) listView.getChildAt(i);
if (listView.isItemChecked(firstVisiblePosition + i)) {
hasChecked = true;
textView.getGlobalVisibleRect(rect);
if (sumRect != null) {
sumRect.union(rect);
}
else {
sumRect = new Rect(rect);
}
}
}
mSumRect = getRectRelTo(sumRect, listView);
if (hasChecked) {
showSelectRegion(mSumRect);
// MEMO: startActionModeは、呼び出しスレッドとは別のスレッドでActionModeのコールバックを呼び出す
mActionMode = listView.startActionMode(new CustomActionModeCallback(), ActionMode.TYPE_FLOATING);
}
else {
showSelectRegion(null);
if (mActionMode != null) {
mActionMode.finish();
}
}
}
}
private class CustomActionModeCallback extends ActionMode.Callback2 {
@Override
public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
outRect.set(mSumRect);
mSumRect = null;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.add(INSERT);
menu.add(DELETE);
menu.add(SHARE);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getTitle().equals(INSERT)) {
insertNewItem();
}
else if (item.getTitle().equals(DELETE)) {
deleteSelection();
}
else if (item.getTitle().equals(SHARE)) {
shareSelections();
}
mode.finish();
showSelectRegion(null);
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
}
![]() |
| 画像1 [テキスト選択時(Android API Lv 22)] |
![]() |
| 画像2 [テキスト選択時(Android API Lv 23)] |
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.sample_text);
textView.setText(R.string.sample_text);
textView.setTextIsSelectable(true);
}
<TextView
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:text="@string/sample_text"
/>
![]() |
| 図3 [アクションモード] |
![]() |
| [実行の様子1] |
private class CustomItemClickedListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
ListView listView = (ListView)parent;
boolean hasChecked = false;
Rect rect = new Rect();
Rect sumRect = null;
int firstVisiblePosition = listView.getFirstVisiblePosition();
for (int i = 0; i < listView.getChildCount(); i++) {
TextView textView = (TextView) listView.getChildAt(i);
if (listView.isItemChecked(firstVisiblePosition + i)) {
hasChecked = true;
textView.getGlobalVisibleRect(rect);
if (sumRect != null) {
sumRect.union(rect);
}
else {
sumRect = new Rect(rect);
}
}
}
mSumRect = getRectRelTo(sumRect, listView);
if (hasChecked) {
showSelectRegion(mSumRect);
// MEMO: startActionModeは、呼び出しスレッドとは別のスレッドでActionModeのコールバックを呼び出す
mActionMode = listView.startActionMode(new CustomActionModeCallback(), ActionMode.TYPE_FLOATING);
}
else {
showSelectRegion(null);
if (mActionMode != null) {
mActionMode.finish();
}
}
}
}
private class CustomActionModeCallback extends ActionMode.Callback2 {
@Override
public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
outRect.set(mSumRect);
mSumRect = null;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.add(INSERT);
menu.add(DELETE);
menu.add(SHARE);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getTitle().equals(INSERT)) {
insertNewItem();
}
else if (item.getTitle().equals(DELETE)) {
deleteSelection();
}
else if (item.getTitle().equals(SHARE)) {
shareSelections();
}
mode.finish();
showSelectRegion(null);
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
}








Android KitKat Hacks プロが教えるテクニック & ツール
株式会社ブリリアントサービス 著
NFC Hacks プロが教えるテクニック&ツール
株式会社ブリリアントサービス 著
Androidプログラミングの教科書
藤田 竜史、要 徳幸、住友 孝郎、日高 正博、小林 慎治、木村 尭海 著
入門Androidアプリケーションテスト
瀬戸 直喜/株式会社ブリリアントサービス 著
実践スマートフォンアプリケーション開発
株式会社ブリリアントサービス、八木 俊広、原 昇平、かわかみ ひろき 著
ジオモバイルプログラミング
郷田まり子/宅間俊志/近藤昭雄 著
ANDROID HACKS プロが教えるテクニック&ツール
株式会社ブリリアントサービス 著