-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
1 changed file
with
378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,378 @@ | ||
#03_10 | ||
|
||
##BLE | ||
- manifest 퍼미션 | ||
```java | ||
<uses-permission android:name="android.permission.BLUETOOTH"/> | ||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> | ||
|
||
<--런타임 퍼미션 처리 해줄 것 --> | ||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> | ||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> | ||
<----> | ||
|
||
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> | ||
|
||
``` | ||
- Permission Control | ||
|
||
```java | ||
import android.Manifest; | ||
import android.annotation.TargetApi; | ||
import android.app.Activity; | ||
import android.content.pm.PackageManager; | ||
import android.os.Build; | ||
import android.widget.Toast; | ||
|
||
/** | ||
* 1. 권한처리를 담당하는 클래스 | ||
* 권한변경시 PERMISSION_ARRAY의 값만 변경해주면 된다 | ||
* | ||
* 2. Activity 에서 사용하기 | ||
* public class Activity implements PermissionControl.Callback{ | ||
* @Override | ||
* protected void onCreate(Bundle savedInstanceState) { | ||
* super.onCreate(savedInstanceState); | ||
* setContentView(R.layout.activity_main); | ||
* // 권한체크 호출 | ||
* PermissionControl.checkPermission(this); | ||
* } | ||
* | ||
* @Override | ||
* public Activity getActivity(){ | ||
* return this; | ||
* } | ||
* @Override | ||
* public void init(){ | ||
* // 초기화 로직 | ||
* } | ||
* | ||
* @Override | ||
* public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||
* super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||
* // 결과처리 | ||
* PermissionControl.onCheckResult(requestCode,grantResults); | ||
* } | ||
* } | ||
*/ | ||
|
||
public class PermissionControl { | ||
// 권한요청코드 | ||
private static final int REQ_PERMISSION = 9760345; | ||
private static Callback callback = null; | ||
|
||
// 요청할 권한 목록 | ||
public static final String PERMISSION_ARRAY[] = { | ||
Manifest.permission.ACCESS_COARSE_LOCATION | ||
, Manifest.permission.ACCESS_FINE_LOCATION | ||
}; | ||
|
||
// 권한체크 함수 | ||
@TargetApi(Build.VERSION_CODES.M) | ||
public static void checkPermission(Callback object){ | ||
callback = object; | ||
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) { | ||
Activity activity = callback.getActivity(); | ||
// 1.1 런타임 권한체크 | ||
// 위에 설정한 권한을 반복문을 돌면서 처리한다... | ||
boolean permCheck = true; | ||
for (String perm : PERMISSION_ARRAY) { | ||
if (activity.checkSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) { | ||
permCheck = false; | ||
break; | ||
} | ||
} | ||
// 1.2 퍼미션이 모두 true 이면 그냥 프로그램 실행 | ||
if (permCheck) { | ||
callback.init(); | ||
} else { | ||
activity.requestPermissions(PERMISSION_ARRAY, REQ_PERMISSION); | ||
} | ||
}else { | ||
callback.init(); | ||
} | ||
} | ||
|
||
// 권한체크 후 콜백처리 | ||
public static void onCheckResult(int requestCode, int[] grantResults) { | ||
boolean checkResult = true; | ||
if(requestCode == REQ_PERMISSION) { | ||
// 권한처리 결과값을 반복문을 돌면서 확인한 후 하나라도 승인되지 않았다면 false를 리턴해준다 | ||
for (int result : grantResults) { | ||
if (result != PackageManager.PERMISSION_GRANTED) { | ||
checkResult = false; | ||
break; | ||
} | ||
} | ||
if(callback != null) { | ||
if (checkResult) { | ||
callback.init(); | ||
} else { | ||
Toast.makeText(callback.getActivity(), "권한을 허용하지 않으시면 프로그램을 실행할 수 없습니다.", Toast.LENGTH_LONG).show(); | ||
} | ||
callback = null; | ||
} | ||
} | ||
} | ||
|
||
interface Callback { | ||
public Activity getActivity(); | ||
public void init(); | ||
} | ||
} | ||
|
||
``` | ||
|
||
|
||
- Main | ||
|
||
```java | ||
|
||
import android.annotation.TargetApi; | ||
import android.app.Activity; | ||
import android.bluetooth.BluetoothAdapter; | ||
import android.bluetooth.BluetoothDevice; | ||
import android.bluetooth.BluetoothGatt; | ||
import android.bluetooth.BluetoothGattCallback; | ||
import android.bluetooth.BluetoothGattCharacteristic; | ||
import android.bluetooth.BluetoothGattService; | ||
import android.bluetooth.BluetoothManager; | ||
import android.bluetooth.BluetoothProfile; | ||
import android.bluetooth.le.BluetoothLeScanner; | ||
import android.bluetooth.le.ScanCallback; | ||
import android.bluetooth.le.ScanFilter; | ||
import android.bluetooth.le.ScanResult; | ||
import android.bluetooth.le.ScanSettings; | ||
import android.content.Context; | ||
import android.content.Intent; | ||
import android.content.pm.PackageManager; | ||
import android.os.Build; | ||
import android.os.Bundle; | ||
import android.os.Handler; | ||
import android.support.annotation.NonNull; | ||
import android.support.v7.app.AppCompatActivity; | ||
import android.util.Log; | ||
import android.widget.Toast; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@TargetApi(21) | ||
public class MainActivity extends AppCompatActivity implements PermissionControl.Callback{ | ||
private BluetoothAdapter mBluetoothAdapter; | ||
private int REQUEST_ENABLE_BT = 1; | ||
private Handler mHandler; | ||
private static final long SCAN_PERIOD = 10000; | ||
private BluetoothLeScanner mLEScanner; | ||
private ScanSettings settings; | ||
private List<ScanFilter> filters; | ||
private BluetoothGatt mGatt; | ||
|
||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
setContentView(R.layout.activity_main); | ||
mHandler = new Handler(); | ||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { | ||
Toast.makeText(this, "BLE Not Supported", | ||
Toast.LENGTH_SHORT).show(); | ||
finish(); | ||
} | ||
// 1. 매니저로 아답터를 가져온다. | ||
final BluetoothManager bluetoothManager = | ||
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); | ||
mBluetoothAdapter = bluetoothManager.getAdapter(); | ||
|
||
} | ||
|
||
@Override | ||
protected void onResume() { | ||
super.onResume(); | ||
PermissionControl.checkPermission(this); | ||
} | ||
|
||
@Override | ||
public Activity getActivity() { | ||
return this; | ||
} | ||
|
||
@Override | ||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||
super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||
PermissionControl.onCheckResult(requestCode,grantResults); | ||
} | ||
|
||
// PermissionControl 에서 호출하는 초기화 함수 | ||
// 상단에서 Permission Control 의 인터페이스를 implements 해야 한다 | ||
@Override | ||
public void init(){ | ||
// 2. 아답터를 사용할 수 없으면 BluetoothAdapter.ACTION_REQUEST_ENABLE 호출 | ||
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { | ||
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); | ||
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); | ||
} else { | ||
// 3. 아답터를 사용할 수 있으면 버전 체크를 해서 21 이상이면 | ||
if (Build.VERSION.SDK_INT >= 21) { | ||
// 3.1 getBluetoothLeScanner() 사용 | ||
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); | ||
settings = new ScanSettings.Builder() | ||
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) | ||
.build(); | ||
filters = new ArrayList<ScanFilter>(); | ||
} | ||
scanLeDevice(true); | ||
} | ||
} | ||
@Override | ||
protected void onPause() { | ||
super.onPause(); | ||
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { | ||
scanLeDevice(false); | ||
} | ||
} | ||
|
||
@Override | ||
protected void onDestroy() { | ||
if (mGatt != null) { | ||
mGatt.close(); | ||
mGatt = null; | ||
} | ||
super.onDestroy(); | ||
} | ||
|
||
@Override | ||
protected void onActivityResult(int requestCode, int resultCode, Intent data) { | ||
if (requestCode == REQUEST_ENABLE_BT) { | ||
if (resultCode == Activity.RESULT_CANCELED) { | ||
//Bluetooth not enabled. | ||
finish(); | ||
return; | ||
} | ||
} | ||
super.onActivityResult(requestCode, resultCode, data); | ||
} | ||
|
||
// 4. 블루투스 스캔 시작 | ||
private void scanLeDevice(final boolean enable) { | ||
|
||
// 4.1 스캔시작 | ||
if (enable) { | ||
|
||
// 4.1.2 10초후에 스캔을 종료하는 sub thread 동작 | ||
mHandler.postDelayed(new Runnable() { | ||
@Override | ||
public void run() { | ||
if (Build.VERSION.SDK_INT < 21) { | ||
mBluetoothAdapter.stopLeScan(mLeScanCallback); | ||
} else { | ||
mLEScanner.stopScan(mScanCallback); | ||
} | ||
} | ||
}, SCAN_PERIOD); | ||
|
||
// 4.1.1 스캔 시작 | ||
if (Build.VERSION.SDK_INT < 21) { | ||
mBluetoothAdapter.startLeScan(mLeScanCallback); | ||
} else { | ||
mLEScanner.startScan(filters, settings, mScanCallback); | ||
} | ||
|
||
// 4.2 스캔 종료 | ||
} else { | ||
if (Build.VERSION.SDK_INT < 21) { | ||
mBluetoothAdapter.stopLeScan(mLeScanCallback); | ||
} else { | ||
mLEScanner.stopScan(mScanCallback); | ||
} | ||
} | ||
} | ||
|
||
// 5. 스캔이 종료된 후에 호출된다 | ||
private ScanCallback mScanCallback = new ScanCallback() { | ||
|
||
// 5.1 블루투스 기기가 하나 검색될경우 | ||
@Override | ||
public void onScanResult(int callbackType, ScanResult result) { | ||
Log.i("callbackType", String.valueOf(callbackType)); | ||
Log.i("result", result.toString()); | ||
BluetoothDevice btDevice = result.getDevice(); | ||
connectToDevice(btDevice); | ||
} | ||
|
||
// 5.2 목록 | ||
@Override | ||
public void onBatchScanResults(List<ScanResult> results) { | ||
for (ScanResult sr : results) { | ||
Log.i("ScanResult - Results", sr.toString()); | ||
} | ||
} | ||
|
||
@Override | ||
public void onScanFailed(int errorCode) { | ||
Log.e("Scan Failed", "Error Code: " + errorCode); | ||
} | ||
}; | ||
|
||
private BluetoothAdapter.LeScanCallback mLeScanCallback = | ||
new BluetoothAdapter.LeScanCallback() { | ||
@Override | ||
public void onLeScan(final BluetoothDevice device, int rssi, | ||
byte[] scanRecord) { | ||
runOnUiThread(new Runnable() { | ||
@Override | ||
public void run() { | ||
Log.i("onLeScan", device.toString()); | ||
connectToDevice(device); | ||
} | ||
}); | ||
} | ||
}; | ||
|
||
// 6. 검색 후 디바이스 연결 | ||
public void connectToDevice(BluetoothDevice device) { | ||
if (mGatt == null) { | ||
// 6.1 디바이스 커넥션시 gattCallback 을 담아서 연결 | ||
mGatt = device.connectGatt(this, false, gattCallback); | ||
scanLeDevice(false);// will stop after first device detection | ||
} | ||
} | ||
|
||
// 7. 디바이스 연결후 호출되는 콜백 | ||
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() { | ||
@Override | ||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { | ||
Log.i("onConnectionStateChange", "Status: " + status); | ||
switch (newState) { | ||
case BluetoothProfile.STATE_CONNECTED: | ||
Log.i("gattCallback", "STATE_CONNECTED"); | ||
gatt.discoverServices(); | ||
break; | ||
case BluetoothProfile.STATE_DISCONNECTED: | ||
Log.e("gattCallback", "STATE_DISCONNECTED"); | ||
break; | ||
default: | ||
Log.e("gattCallback", "STATE_OTHER"); | ||
} | ||
|
||
} | ||
|
||
@Override | ||
public void onServicesDiscovered(BluetoothGatt gatt, int status) { | ||
List<BluetoothGattService> services = gatt.getServices(); | ||
Log.i("onServicesDiscovered", services.toString()); | ||
gatt.readCharacteristic(services.get(1).getCharacteristics().get | ||
(0)); | ||
} | ||
|
||
@Override | ||
public void onCharacteristicRead(BluetoothGatt gatt, | ||
BluetoothGattCharacteristic | ||
characteristic, int status) { | ||
Log.i("onCharacteristicRead", characteristic.toString()); | ||
gatt.disconnect(); | ||
} | ||
}; | ||
|
||
|
||
} | ||
``` |