I write an Android App that should receive data from a Bluetooth low energy device. As a first step I try to scan for devices. Because the scan did not work I reduced my code to a minimum. When I click the checkPermissionBtn Log shows:
BLE_TESTApp: BLUETOOTH grated.
BLE_TESTApp: BLUETOOTH_ADMIN grated.
BLE_TESTApp: ACCESS_COARSE_LOCATION grated.
So I assume all permissions are o.k..
When I click searchBtn Log shows: BLE_TESTApp: Start scan... after 35 sec. BLE_TESTApp: Stop scan.
But leScanCallback is obviously not called. There is no Device found in the Log.
There is definitely at least one Bluetooth LE device available. I checked it with an Bluetooth LE Terminal App from the Appstore.
Wy does scan not found a device?
Here is my code:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "BLE_TESTApp";
private Button searchBtn, checkPermissionBtn;
final int REQ_CODE_BLUETOOTH_PERMISSION = 42;
final int REQ_CODE_BLUETOOTH_ADMIN_PERMISSION = 43;
final int REQ_CODE_COARSE_PERMISSION = 44;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScannerLeScanner;
private final static int REQUEST_ENABLE_BT = 1;
private Handler handler = new Handler();
private static final long INTERVAL = 35000;
private ScanCallback leScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.d(TAG, "Device found");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
searchBtn = findViewById(R.id.searchBtn);
checkPermissionBtn = findViewById(R.id.cpBtn);
checkPermissionBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED){
Log.d(TAG, "BLUETOOTH grated.");
}else{
Log.d(TAG, "BLUETOOTH NOT grated!");
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.BLUETOOTH}, REQ_CODE_BLUETOOTH_PERMISSION);
}
if(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED){
Log.d(TAG, "BLUETOOTH_ADMIN grated.");
}else{
Log.d(TAG, "BLUETOOTH_ADMIN NOT grated!");
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.BLUETOOTH_ADMIN}, REQ_CODE_BLUETOOTH_ADMIN_PERMISSION);
}
if(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED){
Log.d(TAG, "ACCESS_COARSE_LOCATION grated.");
}else{
Log.d(TAG, "ACCESS_COARSE_LOCATION NOT grated!");
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQ_CODE_COARSE_PERMISSION);
}
}
});
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothLeScannerLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothAdapter != null && !bluetoothAdapter.isEnabled()) {
Log.d(TAG, "Bluetooth not enabled!");
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
}
searchBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i(TAG, "Start scan...");
searchBtn.setEnabled(false);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
bluetoothLeScannerLeScanner.startScan(leScanCallback);
}
});
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.i(TAG, "Stop scan.");
searchBtn.setEnabled(true);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
bluetoothLeScannerLeScanner.stopScan(leScanCallback);
}
});
}
}, INTERVAL);
}
});
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQ_CODE_BLUETOOTH_PERMISSION && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.d(TAG, "REQ_CODE_BLUETOOTH_PERMISSION granted.");
}
if(requestCode == REQ_CODE_BLUETOOTH_ADMIN_PERMISSION && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.d(TAG, "REQ_CODE_BLUETOOTH_ADMIN_PERMISSION granted.");
}
if(requestCode == REQ_CODE_COARSE_PERMISSION && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.d(TAG, "REQ_CODE_COARSE_PERMISSION granted.");
}
}
}
And the Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bluetooth_le_test_03">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.Bluetooth_Le_Test_03">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
In addition to what you have done, try adding ACCESS_FINE_LOCATION to the list of permissions. Also have a look at the links below for more information on how to do scanning with Android:-
The ultimate guide to Android BLE
Location needs to be enabled on Android 6.0
Location needs to be enabled on Android 10.0
Turn on BLE scanning without asking for user permission
Related
I'm currently working on an android app to control Bluetooth devices but no available devices show up even though I can find them from the built-in settings>bluetooth menu.
I initially followed the android starter sample out of the box, but couldn't make it work. I then came across several similar posts and tried those as well, but still couldn't make it work. This is my code so far: ```
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_LOCATION_BT = 3;
private static final int REQUEST_ENABLE_BT = 2;
private BluetoothAdapter bluetoothAdapter;
private ArrayList<String> deviceList;
private ActivityMainBinding binding;
private ArrayAdapter<String> listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
requiredSetup();
setupListView();
}
private void setupListView() {
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
deviceList = new ArrayList<>();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
for (BluetoothDevice device : pairedDevices) {
deviceList.add(device.getName());
//String deviceHardwareAddress = device.getAddress(); // MAC address
}
}
listAdapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, deviceList);
binding.listView.setAdapter(listAdapter);
}
private void requiredSetup() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
MainActivity.this,
new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
REQUEST_LOCATION_BT);
}
}
// checking if device supports bluetooth
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
// Device doesn't support Bluetooth
Toast.makeText(this, "Device doesnt support bluetooth", Toast.LENGTH_SHORT).show();
return;
}
// enabling bluetooth if disabled
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BT && resultCode == RESULT_OK) {
Toast.makeText(this, "Bluetooth enabled", Toast.LENGTH_SHORT).show();
} else if (requestCode == REQUEST_ENABLE_BT && resultCode == RESULT_CANCELED) {
Toast.makeText(this, "App requires bluetooth to function", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String permissions[], #NonNull int[] grantResults) {
if (requestCode == REQUEST_LOCATION_BT) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, yay! Start the Bluetooth device scan.
startScan();
} else {
// Alert the user that this application requires the location permission to perform the scan.
Toast.makeText(this, "Requires location permission to work", Toast.LENGTH_SHORT).show();
}
}
}
/**
* initializes device discovery. The list of devices are sent to the broadcast receiver
* The discovery process usually involves an inquiry scan of about 12 seconds,
* followed by a page scan of each device found to retrieve its Bluetooth name.
*/
private void startScan() {
// If we're already discovering, stop it
if (bluetoothAdapter.isDiscovering()) {
Toast.makeText(this, "stopping discovery", Toast.LENGTH_SHORT).show();
bluetoothAdapter.cancelDiscovery();
} else {
try {
// Register for broadcasts when a device is discovered
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);
// Request discover from BluetoothAdapter
bluetoothAdapter.startDiscovery();
binding.progressBar.setVisibility(View.VISIBLE);
} catch (IllegalArgumentException e) {
e.printStackTrace();
Toast.makeText(this, "receivers not registered", Toast.LENGTH_SHORT).show();
}
}
}
/**
* #param view the button view that is pressed and the scanning begins
*/
public void searchDevices(View view) {
// checking if location permissions enabled
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_LOCATION_BT);
} else {
startScan();
}
}
/**
* The BroadcastReceiver that listens for discovered devices and changes the title when
* discovery is finished
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Toast.makeText(getApplicationContext(), "reeiver working", Toast.LENGTH_SHORT).show();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If it's already paired, skip it, because it's been listed already
if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED) {
listAdapter.add(device.getName() + "\n" + device.getAddress());
Log.i("NEW DEVICE", device.getName());
listAdapter.notifyDataSetChanged();
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//setProgressBarIndeterminateVisibility(false);
//setTitle(R.string.select_device);
binding.progressBar.setVisibility(View.INVISIBLE);
if (listAdapter.getCount() == 0) {
Toast.makeText(context, "no devices found", Toast.LENGTH_SHORT).show();
}
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
Toast.makeText(context, "Bluetooth state changed", Toast.LENGTH_SHORT).show();
} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
Toast.makeText(context, "Discovery started", Toast.LENGTH_SHORT).show();
}
}
};
#Override
protected void onDestroy() {
super.onDestroy();
// Make sure we're not doing discovery anymore
if (bluetoothAdapter != null) {
bluetoothAdapter.cancelDiscovery();
}
// Unregister broadcast listeners
this.unregisterReceiver(mReceiver);
}}
This is my manifest permissions:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
This is my repository. It would be very helpful if anyone could shed some light here.
It seems like the code was just fine. The problem was with the target SDK version. Upon changing the version from 30 to 28, it worked. Thanks to MatejC's answer.
I want to write a program that will list the available Bluetooth devices and allow the user to pair with them.
I have pieced together the code below. Unfortunately, the only intent that is called is ACTION_STATE_CHANGED, which occurs when I manually enable/disable Bluetooth on the device I am using for testing.
When I manually enable and disable Bluetooth on the device I am using for testing, it does trigger the intent, because I get the corresponding output. However, none of the other intents, such as "Discovery Started" are triggered.
When I run the adapter.startDiscovery() it always returns false, so I do not think it is looking for devices.
This code always returns the device address as 02:00:00:00:00:00
How can I fix this?
public class MainActivity extends Activity {
private BluetoothAdapter BTAdapter;
private ListView mLvDevices;
public static int REQUEST_BLUETOOTH = 1;
private ArrayList<String> mDeviceList = new ArrayList<String>();
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_bluetooth);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
if (!adapter.isEnabled()) {
Intent enableBluetoothIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBluetoothIntent);
}
ActivityCompat.requestPermissions(this, new String[]
{Manifest.permission.ACCESS_FINE_LOCATION},1);
System.out.println("Discovery"+adapter.startDiscovery());
String mydeviceaddress = adapter.getAddress();
String mydevicename = adapter.getName();
System.out.println(mydevicename + " : " + mydeviceaddress+","+adapter.getState());
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
System.out.println("Action"+action);
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
System.out.println("Started");
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
System.out.println("changed");
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
System.out.println("finished");
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
//bluetooth device found
BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// System.out.println("Found device " + device.getName());
}
}
};
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.easyinfogeek.bluetooth">
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:label="#string/app_name"
android:theme="#android:style/Theme.Holo.Light" >
<activity
android:name="com.easyinfogeek.bluetooth.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Check if your app have the follow permissions before start discovery:
Manifest.permission.ACCESS_FINE_LOCATION;
Manifest.permission.ACCESS_COARSE_LOCATION;
Example:
if(ActivityCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//without permission, attempt to request it.
requestPermissions(Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION, PERM_REQUEST_CODE);
}
Request Permission:
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if(requestCode == PERM_REQUEST_CODE && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//example permission granted
//request startDiscovery again.
}
}
Remember, if you want to enable/disable bluetooth a better way is
BluetoothAdapter.ACTION_REQUEST_ENABLE and
BluetoothAdapter.ACTION_REQUEST_DISABLE.
BluetoothAdapter.ACTION_REQUEST_DISABLE is hide, but you can do it yet.
public static final String ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
//to enable bluetooth
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
// to disable bluetooth
Intent intent = new Intent(ACTION_REQUEST_DISABLE);
startActivityForResult(intent, REQUEST_DISABLE_BT);
I'm trying to scan for wifi networks
WifiWizard2.scan().then(function (results) {
console.log("SCANRESULTS", results);
}, function () {
console.log("TOUGH LUCK");
});
But every time I get an empty array
SCANRESULTS []
How am I doing this wrong?
I can confirm there are at least 20 networks around me.
I'm using an android 7 device if it makes any difference.
Sender class
public class WifiFunction {
private final String tag = WifiFunction.class.getSimpleName();
private WifiManager wifiManager;
public List<WifiDetail> getListofWifi(Context context) {
List<WifiDetail> wifiDetails = new ArrayList<>();
List<ScanResult> results = wifiManager.getScanResults();
Log.d(tag,"Wifi Details " + wifiManager.getScanResults().size());
for (ScanResult result : results) {
wifiDetails.add(new WifiDetail(result.BSSID, result.SSID));
Log.d(tag, result.BSSID + result.SSID);
}
return wifiDetails;
}
public void startScan(Context context)
{
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.startScan();
IntentFilter filter = new IntentFilter();
filter.addAction(SCAN_RESULTS_AVAILABLE_ACTION);
context.registerReceiver(new resultReciever(this),filter);
}
}
Receiver class
public class resultReciever extends BroadcastReceiver {
private WifiFunction wifiFunction;
resultReciever(WifiFunction wifiFunction)
{
this.wifiFunction = wifiFunction;
}
#Override
public void onReceive(Context context, Intent intent) {
Log.d("Receiver","started");
wifiFunction.getListofWifi(context);
}
}
From Main Activity I am just calling:
(new WifiFunction()).startScan(this);
Manifest
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permisiion.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permisiion.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".resultReciever"/>
</application>
Runtime Permission :
private boolean checkPermission() {
List<String> permissionsList = new ArrayList<String>();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(Manifest.permission.ACCESS_WIFI_STATE);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CHANGE_WIFI_STATE) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(Manifest.permission.CHANGE_WIFI_STATE);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (permissionsList.size() > 0) {
ActivityCompat.requestPermissions(this, permissionsList.toArray(new String[permissionsList.size()]),
1);
return false;
}
return true;
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case 1:
(new WifiFunction()).startScan(this);
break;
}
}
This should work. Tried it with the full code.
I´ve got stuck on a problem. I´ve tried some hints from Stack Overflow and other websites.
I would like to program an app, which searches for all Bluetooth devices around and if the matching device has been found (MAC-Address as reference), the app will start a connection.
Therefore I have programmed a test application to test the discovery-function. BUT there is unfortunately a big problem with starting the discovering process on my Android 10 device. I´ve got an older Samsung S3 Mini with Android 4.1.2 (SDK 16) on it, where my code is working fine.
On the Android 10 device startDiscovery() returns false, different to the Android 4 device, which returns true. On the android developer page they say, that false is the returning value, if an error has occurred. The BroadcastReceiver should work fine, because the app on the Android 9 mobile phone detects that a Bluetooth search has been started in the settings. It is only the startDiscovery() function, which the problem is all about (in my opinion).
I am checking all permissions and the Bluetooth state before starting the discovering-process. But i think, it can´t be the written code, because it works perfectly on the older device. Maybe I have something missing for newer devices.
Edit
As Thomas Morris explains below, in Android 10 you need the location turned on by the user. In Android 9 or lower, Thomas Morris answer is correct, because in all SDKs below 29, only the permission is needed and not the enabled location service.
Is there a solution to avoid asking the user to turn on the location themselves?
This is my MainActivity:
public class MainActivity extends AppCompatActivity {
final String TAG = "MainActivity";
BluetoothAdapter bluetoothAdapter;
int status = 0; //0 = start discovering, 1 = cancel discovering
public static final int REQUEST_ACCESS_COARSE_LOCATION = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 11;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED));
registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
checkBluetoothState();
final Button test = findViewById(R.id.testbutton);
test.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(status == 0) {
if(bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
if (checkCoarseLocationPermission()) {
Boolean result = bluetoothAdapter.startDiscovery(); //start discovering and show result of function
Toast.makeText(getApplicationContext(), "Start discovery result: " + result, Toast.LENGTH_SHORT).show();
Log.d(TAG, "Start discovery: " + result);
test.setText("Stop");
status = 1;
}
}else{
checkBluetoothState();
}
}else{
Log.d(TAG,"Stop");
status = 0;
bluetoothAdapter.cancelDiscovery();
test.setText("Start");
}
}
});
checkCoarseLocationPermission();
}
private boolean checkCoarseLocationPermission() {
//checks all needed permissions
if(ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_ACCESS_COARSE_LOCATION);
return false;
}else{
return true;
}
}
private void checkBluetoothState() {
//checks if bluetooth is available and if it´s enabled or not
if(bluetoothAdapter == null){
Toast.makeText(getApplicationContext(), "Bluetooth not available", Toast.LENGTH_SHORT).show();
}else{
if(bluetoothAdapter.isEnabled()){
if(bluetoothAdapter.isDiscovering()){
Toast.makeText(getApplicationContext(), "Device is discovering...", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(), "Bluetooth is enabled", Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(getApplicationContext(), "You need to enabled bluetooth", Toast.LENGTH_SHORT).show();
Intent enabledIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabledIntent, REQUEST_ENABLE_BLUETOOTH);
}
}
}
// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
Log.d(TAG,"Device found: " + deviceName + "|" + deviceHardwareAddress);
Toast.makeText(getApplicationContext(), "FOUND: " + deviceName + "|" + deviceHardwareAddress, Toast.LENGTH_SHORT).show();
}
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
//report user
Log.d(TAG,"Started");
Toast.makeText(getApplicationContext(), "STARTED", Toast.LENGTH_SHORT).show();
}
if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//change button back to "Start"
status = 0;
final Button test = findViewById(R.id.testbutton);
test.setText("Start");
//report user
Log.d(TAG,"Finished");
Toast.makeText(getApplicationContext(), "FINISHED", Toast.LENGTH_SHORT).show();
}
if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
final int extra = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
if(extra == (BluetoothAdapter.STATE_ON)) {
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
Boolean b = bluetoothAdapter.startDiscovery();
Toast.makeText(getApplicationContext(), "Start discovery" + b, Toast.LENGTH_SHORT).show();
}
}
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (bluetoothAdapter.isDiscovering()){
bluetoothAdapter.cancelDiscovery();
}
unregisterReceiver(receiver);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
super.onActivityResult(requestCode,resultCode,data);
if(requestCode == REQUEST_ENABLE_BLUETOOTH){
checkBluetoothState();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults){
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
switch (requestCode){
case REQUEST_ACCESS_COARSE_LOCATION:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(getApplicationContext(),"Permission granted",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(),"Permission denied",Toast.LENGTH_SHORT).show();
}
}
}
}
This is my Manifest:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Enable location permission on the application. To do this go to:
Android phone settings
App & notifications
See all app
Locate your application and select it
Permissions
Allow location slide it on
Then
Turn bluetooth on the device on
Turn location on the device on
Or some code to do it automatically via a popup (call in oncreate method)
public void checkPermission() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
} else {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,}, 1);
}
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
} else {
checkPermission();
}
}
Starting Android 10 the Location service must be enabled otherwise no devices will be found.
I tested BluetoothAdapter.startDiscovery() on Huawei P30 with Android 10 and this method alway return false but really the discovery is started (with Location permission granted and location service enabled). So i don't check the result of startDiscovery() method.
According to the official Android Documentation you need to have both ACCESS_FINE_LOCATION and ACCESS_BACKGROUND_LOCATION permissions to start discovering Bluetooth devices.
/**
* From Android 10 onwards it needs Access Location to search Bluetooth Devices
*/
private void checkForLocationPermission(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
discoverDevices();
} else {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION,}, 1);
}
}
}
/**
* Request Access Location while using the App, because bluetooth need location to start discovering devices
* #param requestCode
* #param permissions
* #param grantResults
*/
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
discoverDevices();
} else {
checkForLocationPermission();
}
}
The above code snippet will help you to request the above permissions from the user.
P.S: Also, these need to specified on the Android Manifest as well.
I think my permissions properly located on AndroidManifest.xml.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ganedu.intent">
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".NextActivity"
android:label="This is next_activity"/>
</application>
</manifest>
I have "android.permission.READ_EXTERNAL_STORAGE" and "android.permission.WRITE_EXTERNAL_STORAGE" right after manifest.
but the error is keep occured.
Caused by: java.lang.SecurityException: Permission Denial: reading
com.android.providers.media.MediaProvider uri
content://media/external/images/media/40 from pid=2473, uid=10073
requires android.permission.READ_EXTERNAL_STORAGE, or
grantUriPermission()
and this is my MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final int IMAGE_GALLERY_REQUEST = 20;
Button btnStart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void gallery_open(View view) {
Intent galleryPickerIntent = new Intent(Intent.ACTION_PICK);
File pictureDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String pictureDirectoryPath = pictureDirectory.getPath();
Uri data = Uri.parse(pictureDirectoryPath);
galleryPickerIntent.setDataAndType(data,"image/*");
startActivityForResult(galleryPickerIntent, IMAGE_GALLERY_REQUEST);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent){
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
if(resultCode == Activity.RESULT_OK){
if(requestCode == IMAGE_GALLERY_REQUEST) {
onSelectedFromGalleryResult(imageReturnedIntent);
}
}
}
private void onSelectedFromGalleryResult(Intent data){
Bitmap bm = null;
if(data != null){
try{
bm = MediaStore.Images.Media.getBitmap(getApplicationContext().getContentResolver(), data.getData());
}catch(IOException e){
e.printStackTrace();
}
//ivImage.setImageBitmap(bm);
ImageView imgView = (ImageView)findViewById(R.id.ivImage);
imgView.setImageBitmap(bm);
}
}
James I suppose you are facing this error on Android 6.0 or onwards. You need to handle the permissions explicitly in these versions.
Please find the android developer link
https://developer.android.com/guide/topics/permissions/requesting.html
You need to provide the run time permission for android marshmallow version
You can add this code in onCreate() or any click events, try this:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1){
// requestPermission();
requestAppPermissions(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE },
R.string.app_name, REQUEST_PERMISSIONS);
//this code will be executed on devices running ICS or later
}
also add this function for requesting the run time permission
public void requestAppPermissions(final String[] requestedPermissions,
final int stringId, final int requestCode) {
mErrorString.put(requestCode, stringId);
int permissionCheck = PackageManager.PERMISSION_GRANTED;
boolean shouldShowRequestPermissionRationale = false;
for (String permission : requestedPermissions) {
permissionCheck = permissionCheck + ContextCompat.checkSelfPermission(this, permission);
shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || ActivityCompat.shouldShowRequestPermissionRationale(this, permission);
}
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale) {
Snackbar.make(findViewById(android.R.id.content), stringId,
Snackbar.LENGTH_INDEFINITE).setAction("GRANT",
new View.OnClickListener() {
#Override
public void onClick(View v) {
ActivityCompat.requestPermissions(DashBoardActivity.this, requestedPermissions, requestCode);
}
}).show();
} else {
ActivityCompat.requestPermissions(this, requestedPermissions, requestCode);
}
} else {
onPermissionsGranted(requestCode);
}
}
This function is called when the permission is granted and you can read the internal and external storage
public void onPermissionsGranted(final int requestCode) {
Toast.makeText(this, "Permissions Received.", Toast.LENGTH_LONG).show();
}
The below code for checking the requested permission result
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
int permissionCheck = PackageManager.PERMISSION_GRANTED;
for (int permission : grantResults) {
permissionCheck = permissionCheck + permission;
}
if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) {
onPermissionsGranted(requestCode);
} else {
Snackbar.make(findViewById(android.R.id.content), mErrorString.get(requestCode),
Snackbar.LENGTH_INDEFINITE).setAction("ENABLE",
new View.OnClickListener() {
#Override
public void onClick(View v) {
}
}).show();
}
}