I am new to Android Studio and to BLE. I am working on a program that will scan for a beacon to read information. I am following this tutorial and the closest solution to my problem I found so far was this post. I am trying to get my app to respond to a button press by showing a list of devices that are available to connect through BLE. I keep running into this error from the LeDeviceListAdapter variable initialization...
Unresolved Reference: LeDeviceListAdapter
I figure that I need to add a class to my code, which I have tried to do directly from this source, but I was unsuccessful.
My question is, how do I add the class to my project if that's the solution and which file in the solution do I add it to? If that's not the solution, can someone help me pinpoint what it is?
Also, could it have something to do with the Android API I'm using for the project?
Here is my code...
view.findViewById<Button>(R.id.button_first).setOnClickListener {
val bluetoothManager: BluetoothManager = activity!!.getSystemService(BluetoothManager::class.java)
val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter
if (bluetoothAdapter == null) {
// Device doesn't support Bluetooth
val text = "This device does not support BlueTooth"
val duration = Toast.LENGTH_SHORT
Toast.makeText(context, text, duration)
return#setOnClickListener
}
if (bluetoothAdapter?.isEnabled == false) {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
val bluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner
var scanning = false
val handler = Handler()
// Stops scanning after 10 seconds.
val SCAN_PERIOD: Long = 10000
val leDeviceListAdapter = LeDeviceListAdapter()
// Device scan callback.
val leScanCallback: ScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
leDeviceListAdapter.addDevice(result.device)
leDeviceListAdapter.notifyDataSetChanged()
}
}
fun scanLeDevice() {
if (!scanning) { // Stops scanning after a pre-defined scan period.
handler.postDelayed({
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}, SCAN_PERIOD)
scanning = true
bluetoothLeScanner.startScan(leScanCallback)
} else {
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}
}
}
}
Thank you in advance!
I don't know if I understood correctly the question, but you need to create a new class file with the LeDeviceListAdapter class definition. Then you need to import it inside the file you are using it.
For some info about BLE you can check my question:
QUESTION
And my answer:
ANSWER
In my answer you can find a class (TagBLE and DataTagBLE) to manage the bluetooth device you find ^^ You can use it and implement a list of them to manage the results from your scan then use a RecyclerView with specific RecyclerView Adapter to show all the devices :D
If you need any help just ask me, I can provice you the code I'm using and explain it to you (: For example I can give you the services and activities I'm using to scan and retrieve BLE :D
Also check this answer:
Answer about LeDeviceListAdapter
I had the code for a service that do the background scan for the BLE and a Base Activity that manage getting the TAG INFO. You can use this for references or import it in your project. It will use a service to scan for the BLE and get the results in the activity
BaseTagsScanService.java:
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
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.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseTagsScanService extends Service {
private static final String TAG = BaseTagsScanService.class.getName();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mBLEScanner;
private ScanCallback mScanCallback;
private boolean mIsScanning;
/** Abstract Methods **/
protected abstract ScanSettings getScanSettings();
protected abstract List<ScanFilter> getScanFilters();
protected abstract void manageScanResult(ScanResult result);
protected abstract void onScanFail(int errorCode);
/** Override Lifecycle Methods **/
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
initBluetooth();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
initBluetooth();
return null;
}
#Override
public void onDestroy() {
unsetAll();
super.onDestroy();
}
/** Protected Methods **//** Init Methods **/
protected void initBluetooth(){
initBluetoothAdapter();
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
initBluetoothLeScanner();
initBluetoothScanCallback();
}
}
/** Getter Methods **/
#Nullable
protected BluetoothAdapter getBluetoothAdapter(){
return mBluetoothAdapter;
}
#Nullable
protected BluetoothManager getBluetoothManager(){
return mBluetoothManager;
}
#Nullable
protected BluetoothLeScanner getBLEScanner(){
return mBLEScanner;
}
#Nullable
protected ScanCallback getScanCallback(){
return mScanCallback;
}
/** BLEScanner Methods **/
protected void unsetBLEScanner(){
if(mBLEScanner != null && mIsScanning && mBluetoothAdapter.isEnabled()){
mBLEScanner.stopScan(mScanCallback);
}
mBLEScanner = null;
}
/** Scan Methods **/
protected boolean isScanning(){
return mIsScanning;
}
protected void setIsScanning(boolean isScanning){
mIsScanning = isScanning;
}
protected boolean startScan(){
boolean ret = false;
if(checkStartScan()){
ret = true;
mIsScanning = true;
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
mBLEScanner.startScan(getScanFilters(), getScanSettings(), mScanCallback);
}
}
return ret;
}
protected boolean stopScan(){
boolean ret = false;
if(checkStopScan()){
ret = true;
mIsScanning = false;
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
mBLEScanner.stopScan(mScanCallback);
}
}
return ret;
}
/** ScanFilter Methods **/
protected List<ScanFilter> buildScanFilters(List<String> macs){
List<ScanFilter> scanFilters = new ArrayList<>();
if(macs != null && macs.size() > 0x0){
scanFilters = new ArrayList<>();
for(String mac : macs){
ScanFilter filter = new ScanFilter
.Builder()
.setDeviceAddress(mac)
.build();
scanFilters.add(filter);
}
} else {
scanFilters.add(new ScanFilter.Builder().build());
}
return scanFilters;
}
/** Private Methods **//** Init Methods **/
private void initBluetoothAdapter(){
mBluetoothManager = (BluetoothManager) getBaseContext().getSystemService(Context.BLUETOOTH_SERVICE);
if(mBluetoothManager != null){
mBluetoothAdapter = mBluetoothManager.getAdapter();
}
}
private void initBluetoothLeScanner(){
mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
}
private void initBluetoothScanCallback(){
mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
manageScanResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
onScanFail(errorCode);
Log.e("CheckInOut_Admin", "onScanFailed - errorCode = " + errorCode);
}
};
}
/** Unset Methods **/
private void unsetAll(){
unsetBLEScanner();
unsetBluetoothAdapter();
}
private void unsetBluetoothAdapter(){
mBluetoothAdapter = null;
mBluetoothManager = null;
}
/** Check Scan Methods **/
private boolean checkStartScan(){
return !mIsScanning && mBLEScanner != null;
}
private boolean checkStopScan(){
return mIsScanning && mBLEScanner != null;
}
}
TagsScanService.java:
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.emax.it.lib_base.BaseConstants;
import com.emax.it.lib_base.BaseEnvironment;
import com.emax.it.lib_base.modules.handlers.MessageHandler;
import com.emax.it.lib_base.modules.handlers.MessageManager;
import com.emax.it.lib_base.ui.receivers.BluetoothStatusChangedReceiver;
import com.emax.it.lib_base.ui.receivers.ProviderStatusChangedReceiver;
import com.emax.it.lib_ble.base_activities.BaseTagsScanActivity;
import com.emax.it.lib_ble.models.TagBle;
import com.emax.it.lib_ble.services.base.BaseTagsScanService;
import com.emax.it.lib_ble.utils.BluetoothUtils;
import java.util.ArrayList;
import java.util.List;
public class TagsScanService extends BaseTagsScanService
implements BluetoothStatusChangedReceiver.IOnBluetoothStatusChanged,
ProviderStatusChangedReceiver.IOnProviderStatusChanged,
MessageHandler.IOnHandleMessage {
private static final String TAG = TagsScanService.class.getSimpleName();
// Messages Whats
public static final int MSG_WHAT_SCAN_START = 0x5C1;
public static final int MSG_WHAT_SCAN_STOP = 0x5C2;
public static final int MSG_WHAT_BLUETOOTH_OFF = 0x5C3;
public static final int MSG_WHAT_GPS_OFF = 0x5C4;
public static final int MSG_WHAT_BLE_TAG = 0x5C5;
// Messages Args
public static final int MSG_ARG_ONE_FAIL = 0x0;
public static final int MSG_ARG_ONE_SUCCESS = 0x1;
private BluetoothStatusChangedReceiver mBluetoothReceiver;
private ProviderStatusChangedReceiver mProviderReceiver;
private MessageManager mMessenger;
/** Override BaseTagsScanService Methods **/
#Override
protected ScanSettings getScanSettings(){
return new ScanSettings
.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
}
#Override
protected List<ScanFilter> getScanFilters(){
return new ArrayList<>();
}
#Override
protected void manageScanResult(ScanResult result) {
if(result != null){
BluetoothDevice device = result.getDevice();
if(!TextUtils.isEmpty(device.getAddress())){
sendTag(TagBle.initInstanceFromScanResult(result, BluetoothUtils.getBeaconTxPower(result)), result.getRssi());
}
}
}
#Override
protected void onScanFail(int errorCode) {
switch (errorCode){
case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
if(getBLEScanner() != null){
getBLEScanner().stopScan(getScanCallback());
getBLEScanner().startScan(getScanFilters(), getScanSettings(), getScanCallback());
}
break;
case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
if(getBluetoothAdapter() != null){
getBLEScanner().stopScan(getScanCallback());
getBluetoothAdapter().disable();
new Handler().postDelayed(() -> {
getBluetoothAdapter().enable();
new Handler().postDelayed(() -> getBLEScanner().startScan(getScanFilters(), getScanSettings(), getScanCallback()), BaseConstants.MILLIS_QUARTER_SECOND);
}, BaseConstants.MILLIS_QUARTER_SECOND);
}
break;
}
}
/** Override Lifecycle Methods **/
#Override
public void onCreate() {
super.onCreate();
initOnCreate();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
initMessageManager();
return mMessenger.getMsgReceiver().getBinder();
}
#Override
public void onDestroy() {
unsetBluetoothStatusReceiver();
unsetProviderStatusReceiver();
super.onDestroy();
}
/** Override BluetoothStatusChangedReceiver.IOnBluetoothStatusChanged Methods **/
public void onBluetoothStatusChanged(int status){
if(status == BluetoothAdapter.STATE_TURNING_OFF){
stopScan();
sendBluetoothOff();
} else if(BluetoothUtils.checkBLE(this)){
initBluetooth();
}
}
/** Override ProviderStatusChangedReceiver.IOnProviderStatusChanged Methods **/
#Override
public void onProviderStatusChanged(int status) {
if(status == ProviderStatusChangedReceiver.IOnProviderStatusChanged.STATUS_DISABLED){
stopScan();
sendGpsOff();
} else if(BluetoothUtils.checkBLE(this)){
initBluetooth();
}
}
/** Override IOnHandleMessage Callback Methods **/
#Override
public void onHandleMessage(Message msg) {
switch(msg.what){
case MSG_WHAT_SCAN_START:
boolean started = startScan();
sendScanStarted(started ? MSG_ARG_ONE_SUCCESS : MSG_ARG_ONE_FAIL, msg.arg2);
break;
case MSG_WHAT_SCAN_STOP:
boolean stopped = stopScan();
sendScanStopped(stopped ? MSG_ARG_ONE_SUCCESS : MSG_ARG_ONE_FAIL, msg.arg2);
break;
}
}
/** Protected Methods **/
protected <T extends Parcelable> void sendTag(T tag, int rssi){
if(mMessenger != null){
Bundle data = new Bundle();
data.putParcelable(BaseTagsScanActivity.EXTRA_BLE_TAG, tag);
data.putInt(BaseTagsScanActivity.EXTRA_RSSI_VALUE, rssi);
mMessenger.sendMessage(MSG_WHAT_BLE_TAG, 0x0, 0x0, data);
}
}
protected MessageManager getMessenger(){
return mMessenger;
}
/** Private Methods **//** Init Methods **/
private void initOnCreate(){
initRegisterBluetoothStatusChangedReceiver();
initRegisterProviderStatusChangedReceiver();
}
private void initMessageManager(){
mMessenger = new MessageManager(this);
}
private void initRegisterBluetoothStatusChangedReceiver(){
initBluetoothAdapterReceiver();
registerBluetoothAdapterReceiver();
}
private void initRegisterProviderStatusChangedReceiver(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
initProviderStatusChangedReceiver();
registerProviderStatusChangedReceiver();
} else {
mProviderReceiver = null;
}
}
private void initBluetoothAdapterReceiver(){
mBluetoothReceiver = new BluetoothStatusChangedReceiver(this);
}
private void initProviderStatusChangedReceiver(){
mProviderReceiver = new ProviderStatusChangedReceiver(this);
}
/** Register Receivers Methods **/
private void registerBluetoothAdapterReceiver(){
if(mBluetoothReceiver != null && !mBluetoothReceiver.isRegistered()) {
registerReceiver(mBluetoothReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
mBluetoothReceiver.setIsRegistered(true);
}
}
private void registerProviderStatusChangedReceiver(){
if(mProviderReceiver != null && !mProviderReceiver.isRegistered()){
registerReceiver(mProviderReceiver, new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION));
mProviderReceiver.setIsRegistered(true);
}
}
/** Unset Methods **/
private void unsetBluetoothStatusReceiver(){
if(mBluetoothReceiver != null) {
try {
unregisterReceiver(mBluetoothReceiver);
} catch (RuntimeException rE) {
BaseEnvironment.onExceptionLevelLow(TAG, rE);
}
mBluetoothReceiver = null;
}
}
private void unsetProviderStatusReceiver(){
if(mProviderReceiver != null){
try {
unregisterReceiver(mProviderReceiver);
} catch (RuntimeException rE){
BaseEnvironment.onExceptionLevelLow(TAG, rE);
}
mProviderReceiver = null;
}
}
/** Messenger Methods **/
private void sendScanStarted(int arg1, int arg2){
if(mMessenger != null ){
mMessenger.sendMessage(MSG_WHAT_SCAN_START, arg1, arg2, null);
}
}
private void sendScanStopped(int arg1, int arg2){
if(mMessenger != null){
mMessenger.sendMessage(MSG_WHAT_SCAN_STOP, arg1, arg2,null);
}
}
private void sendBluetoothOff(){
if(mMessenger != null){
mMessenger.sendMessage(MSG_WHAT_BLUETOOTH_OFF, 0x0, 0x0, null);
}
}
private void sendGpsOff(){
if(mMessenger != null){
mMessenger.sendMessage(MSG_WHAT_GPS_OFF, 0x0, 0x0, null);
}
}
}
BaseTagsScanActivity.java:
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.view.MenuItem;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.emax.it.lib_base.modules.handlers.MessageHandler;
import com.emax.it.lib_base.modules.handlers.MessageManager;
import com.emax.it.lib_base.ui.activities.BaseAppCompatActivity;
import com.emax.it.lib_ble.LibBleConstants;
import com.emax.it.lib_ble.R;
import com.emax.it.lib_ble.models.TagBle;
import com.emax.it.lib_ble.services.TagsScanService;
import com.emax.it.lib_ble.utils.BluetoothUtils;
public abstract class BaseTagsScanActivity extends BaseAppCompatActivity
implements MessageHandler.IOnHandleMessage,
BluetoothUtils.ICheckStatusBLE {
private static final String TAG = BaseTagsScanActivity.class.getSimpleName();
private static final String EXTRA = TAG + ".extra";
// Intents Extras
public static final String EXTRA_BLE_TAG = EXTRA + ".tagBLE";
public static final String EXTRA_RSSI_VALUE = EXTRA + ".valueRSSI";
protected Handler mScanHandler;
protected MessageManager mMessenger;
protected BLEScanServiceConnection mServiceConnection;
protected boolean mBound;
/** Override Lifecycle Methods **/
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initOnCreate();
}
#Override
protected void onStart() {
super.onStart();
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
#Override
protected void onStop() {
super.onStop();
unbindToService();
}
#Override
protected void onDestroy() {
if(mScanHandler != null){
mScanHandler.removeCallbacksAndMessages(null);
mScanHandler = null;
}
mMessenger = null;
unbindToService();
stopTagsScanService();
super.onDestroy();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == LibBleConstants.REQ_PERMISSION_BLUETOOTH_LOCATION) {
if (grantResults.length > 0x0 && grantResults[0x0] == PackageManager.PERMISSION_GRANTED) {
BluetoothUtils.doFullCheckEnableBLE(this, this);
} else {
Toast.makeText(this, getString(R.string.error_permission_not_granted), Toast.LENGTH_SHORT).show();
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode){
case LibBleConstants.REQ_CODE_ENABLE_BT:
if(resultCode == RESULT_OK){
BluetoothUtils.doFullCheckEnableBLE(this, this);
} else {
finish();
}
break;
case LibBleConstants.REQ_CODE_BLE_ENABLE_GPS:
LocationManager lM = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (lM != null && lM.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
onSuccessCheckBLE();
} else {
Toast.makeText(this, getString(R.string.error_scan_tags_require_gps), Toast.LENGTH_LONG).show();
finish();
}
break;
}
}
/** Override Actionbar Listener Methods **/
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
/** Override ICheckStatusBLE Methods **/
#Override
public void onCheckStatusBleOk(){
onSuccessCheckBLE();
}
#Override
public void onCheckStatusBleError(int err) {
finish();
}
/** Protected Methods **/
protected void callBaseOnCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}
protected void callBaseOnStart(){
super.onStart();
}
protected void initOnCreate(){
if(getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
mBound = false;
initMyServiceConnection();
initScanHandler();
}
protected void initScanHandler(){
mScanHandler = new Handler();
}
/** Service Methods **/
protected void stopTagsScanService(){
stopService(new Intent(this, TagsScanService.class));
}
protected void bindToTagsScanService(){
bindService(new Intent(this, TagsScanService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
protected void unbindToService(){
if(mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
/** Scan Methods **/
protected void startScan(int arg1, int arg2, #Nullable Bundle data){
startScan(TagsScanService.MSG_WHAT_SCAN_START, arg1, arg2, data);
}
protected void startScan(int what, int arg1, int arg2, #Nullable Bundle data){
if(mMessenger != null){
mMessenger.sendMessage(what, arg1, arg2, data);
}
}
protected void stopScan(int arg1, int arg2, #Nullable Bundle data){
if(mMessenger != null){
mMessenger.sendMessage(TagsScanService.MSG_WHAT_SCAN_STOP, arg1, arg2, data);
}
}
protected void startScanHandler(Runnable runnable, long time){
if(mScanHandler == null){
initScanHandler();
}
mScanHandler.postDelayed(runnable, time);
}
/** Check Methods **/
protected boolean checkStartStopScan(){
boolean ret = false;
boolean checkBLE = BluetoothUtils.checkBLE(this);
if(mBound && checkBLE){
ret = true;
} else if(!mBound && checkBLE){
showMessageServiceError();
} else {
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
return ret;
}
/** Handle Message Methods **/
protected void onHandleMessageBluetoothOff(){
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
protected void onHandleMessageGpsOff(){
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
protected <T extends Parcelable> T getTagBleFromBundle(Bundle data){
if(data != null && data.containsKey(EXTRA_BLE_TAG)){
return (T) data.getParcelable(EXTRA_BLE_TAG);
}
return null;
}
protected int getRssiFromBundle(Bundle data){
if(data != null && data.containsKey(BaseTagsScanActivity.EXTRA_RSSI_VALUE)){
return data.getInt(BaseTagsScanActivity.EXTRA_RSSI_VALUE, 0x0);
}
return 0x0;
}
/** Messages Methods **/
protected void showMessageServiceError(){
Toast.makeText(this, R.string.error_scan_service_not_started, Toast.LENGTH_SHORT).show();
}
protected void showMessageScanStartError(){
Toast.makeText(this, R.string.error_start_scan, Toast.LENGTH_LONG).show();
}
protected void showMessageScanStopError(){
Toast.makeText(this, R.string.error_stop_scan, Toast.LENGTH_SHORT).show();
}
/** Private Methods **//** Init Methods **/
private void initMyServiceConnection(){
mServiceConnection = new BLEScanServiceConnection();
}
private void initMyMessenger(IBinder iBinder){
mMessenger = new MessageManager(this, iBinder);
mMessenger.sendHandshake();
}
/** Service Methods **/
private void onSuccessCheckBLE(){
bindToTagsScanService();
}
/** Private Classes **/
protected class BLEScanServiceConnection implements ServiceConnection {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
initMyMessenger(iBinder);
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mMessenger = null;
mBound = false;
}
}
}
Rest of code you have the classes in my answer above and the message handlers in this answer:
Message Handlers between service and activity
Looks like you haven't implemented correctly,
You first need to create a new class file with the name "LeDeviceListAdapter" in the same package.
Once you created this class, you need to import this class in your activity/fragment.
This adapter is using BaseAdapter class.
// Adapter for holding devices found through scanning.
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList<BluetoothDevice> mLeDevices;
private LayoutInflater mInflator;
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList<BluetoothDevice>();
mInflator = DeviceScanActivity.this.getLayoutInflater();
}
public void addDevice(BluetoothDevice device) {
if(!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
public BluetoothDevice getDevice(int position) {
return mLeDevices.get(position);
}
public void clear() {
mLeDevices.clear();
}
#Override
public int getCount() {
return mLeDevices.size();
}
#Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.listitem_device, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
BluetoothDevice device = mLeDevices.get(i);
final String deviceName = device.getName();
if (deviceName != null && deviceName.length() > 0)
viewHolder.deviceName.setText(deviceName);
else
viewHolder.deviceName.setText(R.string.unknown_device);
viewHolder.deviceAddress.setText(device.getAddress());
return view;
}
}
Layout File listitem_device.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="#+id/device_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24dp"/>
<TextView android:id="#+id/device_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12dp"/>
</LinearLayout>
ViewHolder class inside the Adapter class file
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
}
I'm trying get the Camera to autofocus using the CameraSource class from Android Mobile Vision API.
I've activated autofocus as follows:
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(640, 480)
.setAutoFocusEnabled(true)
.setRequestedFps(24.0f)
.build();
But the SurfaceView rendering the camera is often blurred.
Here my full Activity's code:
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.IOException;
import java.util.Arrays;
public class MainActivity extends Activity {
private BarcodeDetector barcodeDetector;
private CameraSource cameraSource;
private SurfaceView cameraView;
private TextView barcodeInfo;
public static int REQUEST_PERMISSION_CAMERA = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraView = (SurfaceView) findViewById(R.id.camera_view);
barcodeInfo = (TextView) findViewById(R.id.code_info);
barcodeDetector =
new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.QR_CODE)
.build();
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(640, 480)
.setAutoFocusEnabled(true)
.setRequestedFps(24.0f)
.build();
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION_CAMERA);
return;
} else {
cameraSource.start(cameraView.getHolder());
}
} else {
cameraSource.start(cameraView.getHolder());
}
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodeInfo.post(new Runnable() { // Use the post method of the TextView
public void run() {
barcodeInfo.setText( // Update the TextView
barcodes.valueAt(0).displayValue
);
}
});
}
}
});
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
int[] checkValues = new int[1];
if (requestCode == REQUEST_PERMISSION_CAMERA) {
if (Arrays.equals(grantResults, checkValues)) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
#Override
protected void onDestroy() {
super.onDestroy();
cameraSource.release();
barcodeDetector.release();
}
}
I have a SurfaceView showing a CameraSource and I want the camera to focus automatically. Also, when the user taps the SurfaceView, I want the camera to focus on the tapped area. The autofocus is already enabled, but the manual focus is not and I dont know how to add that, any tips?
My code so far:
public class MyActivity extends AppCompatActivity {
SurfaceView cameraPreview;
BarcodeDetector barcodeDetector;
CameraSource cameraSource;
final Integer requestCameraPermissionID = 1001;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity_layout);
final int height = getSurfaceViewHeight();
final int width = getSurfaceViewWidth();
cameraPreview = findViewById(R.id.surfaceview_scanner);
barcodeDetector = new BarcodeDetector.Builder(AddScanningActivity.this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
cameraSource = new CameraSource.Builder(AddScanningActivity.this, barcodeDetector)
.setAutoFocusEnabled(false)
.setRequestedPreviewSize(width, height)
.build();
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
// handle detections
}
});
cameraPreview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// handle manual focus here?
}
});
cameraPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(AddScanningActivity.this,
new String[]{Manifest.permission.CAMERA}, requestCameraPermissionID);
return;
}
try {
cameraSource.start(cameraPreview.getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
}, 100);
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
cameraSource.stop();
}
});
}
}
If it is not possible to have autofocus and manual focus in the same SurfaceView, I would rather have manual focus than autofocus.
As you are making barcode app i will suggest you to use FOCUS_MODE_FIXED,
or
FOCUS_MODE_MACRO
package com.example.yaumanualcamera;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Camera;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
TextureView textureview;
CameraDevice cameraDevice;
String cameraId;
Size imageDimensions;
CaptureRequest.Builder captureRequestBuilder;
CameraCaptureSession cameraSession;
Handler backgroundHandler;
HandlerThread handleThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textureview = (TextureView) findViewById(R.id.texture);
textureview.setSurfaceTextureListener(surfaceTextureListener);
}
TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
try {
openCamera();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
private void openCamera() throws CameraAccessException {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
cameraId = cameraManager.getCameraIdList()[0];
CameraCharacteristics cc = cameraManager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
imageDimensions = map.getOutputSizes(SurfaceTexture.class)[0];
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
cameraManager.openCamera(cameraId, stateCallback, null);
}
CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(#NonNull CameraDevice camera) {
cameraDevice = camera;
try {
startCameraPreview();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onDisconnected(#NonNull CameraDevice camera) {
cameraDevice.close();
}
#Override
public void onError(#NonNull CameraDevice camera, int error) {
cameraDevice.close();
cameraDevice = null;
}
};
private void startCameraPreview() throws CameraAccessException {
SurfaceTexture texture = textureview.getSurfaceTexture();
texture.setDefaultBufferSize(imageDimensions.getWidth(), imageDimensions.getHeight());
Surface surface = new Surface(texture);
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
System.out.println("Focus 1 ====== "+captureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE));
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession session) {
if (cameraDevice == null) {
return;
}
cameraSession = session;
try {
updatePreview();
} catch (CameraAccessException e) {
}
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession session) {
}
}, null);
}
private void initPreview() {
float valueAF;
valueAF = 10.0f;
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE,CameraMetadata.CONTROL_MODE_AUTO);
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_OFF);
captureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, valueAF);
}
private void updatePreview() throws CameraAccessException {
if (cameraDevice == null) {
return;
}
initPreview();
System.out.println("Focus 2 ====== "+captureRequestBuilder.get(CaptureRequest.LENS_FOCUS_DISTANCE));
cameraSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);
}
#Override
protected void onResume() {
super.onResume();
startBackgroundThread();
if (textureview.isAvailable()) {
try {
openCamera();
} catch (CameraAccessException e) {
e.printStackTrace();
}
} else {
textureview.setSurfaceTextureListener(surfaceTextureListener);
}
}
private void startBackgroundThread() {
handleThread = new HandlerThread("CAMERA BACKGROUND");
handleThread.start();
backgroundHandler = new Handler(handleThread.getLooper());
}
#Override
protected void onPause() {
try {
stopBackgroundThread();
} catch (InterruptedException e) {
e.printStackTrace();
}
super.onPause();
}
private void stopBackgroundThread() throws InterruptedException {
handleThread.quitSafely();
handleThread.join();
backgroundHandler = null;
handleThread = null;
}
}
I'm trying to create a barcode reader. The program works perfectly when the barcode is big. To read small barcodes i will need autofocus enabled.
I added setAutoFocusEnabled(true) to cameraSource, but it doesnt work.
How can i fix the following code to get autofocus working?
My testting device is a samsung galaxy J1 sm111m android 5.1.1 API 22
package com.gutimore.android.pdf417;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
SurfaceView cameraPreview;
TextView txtResult;
BarcodeDetector barcodeDetector;
CameraSource cameraSource;
final int RequestCameraPermissionID = 1001;
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case RequestCameraPermissionID: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
try {
cameraSource.start(cameraPreview.getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
}
break;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraPreview = findViewById(R.id.cameraPreview);
txtResult = findViewById(R.id.txtResult);
barcodeDetector = new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.PDF417)
.build();
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(640, 480)
.setAutoFocusEnabled(true)
.build();
//Add Event
cameraPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA},RequestCameraPermissionID);
return;
}
try {
cameraSource.start(cameraPreview.getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> qrcodes = detections.getDetectedItems();
if(qrcodes.size() != 0)
{
txtResult.post(new Runnable() {
#Override
public void run() {
//Create vibrate
Vibrator vibrator = (Vibrator)getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(1000);
txtResult.setText(qrcodes.valueAt(0).displayValue);
}
});
}
}
});
}
}
Use CamaraSource from (link)
package com.google.android.gms.samples.vision.barcodereader.ui.camera;
And initialize, as below
CameraSource camera = new CameraSource.Builder(requireContext(), barcodeDetector)
.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)
.build();
But on some old samsung devices autofocus still won't be working. Better use ZXing
I am trying to make a text scanner application with camera that recognizes text and shows in screen.But instead of showing camera it shows a black screen. How can I solve this.
My code is here
package com.myapp.game.easynepalirecharge;
import android.Manifest;
import android.app.ActionBar;
import android.content.pm.PackageManager;
import android.graphics.Camera;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.widget.TextView;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.text.TextBlock;
import com.google.android.gms.vision.text.TextRecognizer;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
SurfaceView cameraView;
TextView textView;
CameraSource cameraSource;
final int REQUESTCAMERAPERMISSION = 105;
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull final int[] grantResults) {
switch (requestCode) {
case REQUESTCAMERAPERMISSION:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(getApplication(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraView = (SurfaceView) findViewById(R.id.surfaceView);
textView = (TextView) findViewById(R.id.textView);
TextRecognizer textRecognizer = new TextRecognizer.Builder(getApplicationContext()).build();
if (!textRecognizer.isOperational()) {
Log.v("haha", "error not operational");
} else {
cameraSource = new CameraSource.Builder(getApplicationContext(), textRecognizer).
setFacing(CameraSource.CAMERA_FACING_BACK)
.setRequestedPreviewSize(3840, 2160)
.setRequestedFps(2.0f)
.setAutoFocusEnabled(true)
.build();
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, REQUESTCAMERAPERMISSION);
cameraSource.start(cameraView.getHolder());
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
textRecognizer.setProcessor(new Detector.Processor<TextBlock>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<TextBlock> detections) {
final SparseArray<TextBlock> items = detections.getDetectedItems();
if (items.size() != 0) {
textView.post(new Runnable() {
#Override
public void run() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i <= items.size(); i++) {
TextBlock item = items.valueAt(i);
stringBuilder.append(item.getValue());
stringBuilder.append("\n");
}
textView.setText(stringBuilder.toString());
}
});
}
}
}
);
}
}
}
and another thing I wanna ask. Which one is better. This or the tess two library?
have you stopped camera properly. Try to do camera functions in thread so that your single task does not be expensive. The reason i thought problem is that get your permission at run time.
try this
https://developer.android.com/training/permissions/requesting.html