I've created some java function in the MainActivity.java. I then add some Toast to determine the sequence of the functions. And then, the sequence of the functions are confusing. onCreate() started first, which is totally normal, followed by askCoarsePermission(). Then the weird thing happen, onResume() is then followed and proceed to call askBluetoothPermission(). What happen in askCoarsePermission() that it's able to call onResume()?
The sequence:
1. onCreate()
2. askCoarsePermission()
3. onResume()
4. askBluetoothPermission()
5. -- stops.. no functions is then being invoked --
I tried to check what are the function that can calls onResume(), but none of the askCoarsePermission() did call that function.
Here are the code for MainActivity.java:
package com.example.bluetoothscanexample;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;
public class MainActivity extends ListActivity {
private static int REQUEST_CODE_COARSE_PERMISSION = 1;
private static int REQUEST_CODE_BLUETOOTH_PERMISSION = 2;
// make scan period 10 seconds before starting the new one
private static int SCAN_PERIOD = 100;
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mBluetoothLeScanner;
private com.example.bluetoothscanexample.adapter.BluetoothLeListAdapter mLeDeviceListAdapter;
// No idea what is this lol
private Handler mHandler;
private boolean mScanning;
private ScanCallback mBluetoothScanCallBack = new ScanCallback() {
#Override
public void onScanResult(int callbackType, final ScanResult result) {
Toast.makeText(MainActivity.this, "onScanResult here...", Toast.LENGTH_LONG).show();
super.onScanResult(callbackType, result);
runOnUiThread(new Runnable() {
#Override
public void run() {
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
#Override
public void onScanFailed(int errorCode) {
Toast.makeText(MainActivity.this, "onScanFailed here...", Toast.LENGTH_LONG).show();
super.onScanFailed(errorCode);
Toast.makeText(MainActivity.this, "Scan Failed: Please try again...", Toast.LENGTH_LONG).show();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mLeDeviceListAdapter = new com.example.bluetoothscanexample.adapter.BluetoothLeListAdapter(this);
// Add this line to make scanning work!!!
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
// check if this phone support Bluetooth Low Energy
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
Toast.makeText(this,"ble_not_supported",Toast.LENGTH_SHORT);
finish();
}
Toast.makeText(MainActivity.this, "onCreate here..", Toast.LENGTH_LONG).show();
askCoarsePermission();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
Toast.makeText(MainActivity.this, "onActivityResult here...", Toast.LENGTH_LONG).show();
super.onActivityResult(requestCode,resultCode,data);
if(requestCode == REQUEST_CODE_BLUETOOTH_PERMISSION){
askCoarsePermission();
}
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
scanBluetoothDevices(true);
}
#Override
protected void onResume(){
Toast.makeText(MainActivity.this, "onResume here...", Toast.LENGTH_LONG).show();
super.onResume();
setListAdapter(mLeDeviceListAdapter);
askBluetoothPermission();
}
#Override
protected void onPause(){
Toast.makeText(MainActivity.this, "onPause here...", Toast.LENGTH_LONG).show();
super.onPause();
mLeDeviceListAdapter.clear();
}
private void askCoarsePermission(){
Toast.makeText(MainActivity.this, "askCoarsePermission here...", Toast.LENGTH_LONG).show();
if(this.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)!= PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION},REQUEST_CODE_COARSE_PERMISSION);
}
}
private void askBluetoothPermission() {
Toast.makeText(MainActivity.this, "askBluetoothPermission here...", Toast.LENGTH_LONG).show();
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_CODE_BLUETOOTH_PERMISSION);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){
Toast.makeText(MainActivity.this, "onRequestPermissionResult here...", Toast.LENGTH_LONG).show();
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
if(requestCode == REQUEST_CODE_COARSE_PERMISSION){
Toast.makeText(this,"Coarse Permission Granted",Toast.LENGTH_LONG).show();
}
}
private void scanBluetoothDevices(boolean enable){
Toast.makeText(MainActivity.this, "scanBluetoothDevices here...", Toast.LENGTH_LONG).show();
if(enable){
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothLeScanner.stopScan(mBluetoothScanCallBack);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothLeScanner.startScan(mBluetoothScanCallBack);
}else{
mScanning = false;
mBluetoothLeScanner.stopScan(mBluetoothScanCallBack);
}
}
}
One more issue followed. Why does the java stops at askBluetoothPermission() and no other function runs? I am so confusing here. Someone please enlighten me.
this is the regular Activity life cycle, first onCreate -> onStart -> onResume.
To understand better take a look in this docs
Related
I want make some project where Android can scan nearby Beacon/BLE and send it using MQTT. But I want the service to work in the background if the service work in the foreground it will interrupt the scanning process when screen is off.
This is my code for scanning:
package com.example.mqtt_active;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import java.nio.charset.StandardCharsets;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private Button turnon, changeLayout;
MqttAndroidClient client;
private boolean state=false;
private BluetoothAdapter bluetoothAdapter;
public static final int REQUEST_ACCESS_COARSE_LOCATION = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 11;
public static String mqtt_server,mqtt_port,mqtt_id;
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_main);
Log.d("Logger", "On Create Android");
turnon = findViewById(R.id.turnon);
changeLayout = findViewById(R.id.mqttSet);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
textView = findViewById(R.id.textView4);
textView.setText("id "+mqtt_id+" port "+mqtt_port+" server "+mqtt_server);
client = new MqttAndroidClient(this.getApplicationContext(), "tcp://"+mqtt_server+":"+mqtt_port,mqtt_id);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
stateCheck();
Log.d("Logger", "State Check");
handler.postDelayed(this, 1000);
}
}, 1000);
// final Handler handlerStop = new Handler();
// handlerStop.postDelayed(new Runnable() {
// #Override
// public void run() {
// bluetoothAdapter.cancelDiscovery();
// Log.d("Logger", "Cancel Dsicovery");
// handlerStop.postDelayed(this, 2000);
// }
// }, 2000);
turnon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!state){
turnon.setText("Turn Off");
Log.d("Logger", "Turn On State");
// if (bluetoothAdapter!=null & bluetoothAdapter.isEnabled()) {
// if(checkCoarsePermission()){
// bluetoothAdapter.startDiscovery();
// }
// }
if(mqtt_server!=null||mqtt_id!=null||mqtt_port!=null){
try {
Log.d("Logger", "Try ");
IMqttToken token = client.connect();
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d("Logger", "Connect MQTT");
Toast.makeText(MainActivity.this,"connected!!",Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.d("Logger", "Connect Failed");
Toast.makeText(MainActivity.this,"connection failed!!",Toast.LENGTH_LONG).show();
}
});
} catch (MqttException e) {
e.printStackTrace();
Log.d("Logger", "error"+e);
}}
state = true;
}else{
turnon.setText("Turn On");
state = false;
// bluetoothAdapter.cancelDiscovery();
}
}
});
changeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,MqttActivity.class));
}
});
}
public void stateCheck(){
if (state){
if (bluetoothAdapter!=null & bluetoothAdapter.isEnabled()) {
if(checkCoarsePermission()){
Log.d("Logger", "Discover");
bluetoothAdapter.startDiscovery();
}
}
}
// else {
// Log.d("Logger", "Cancel");
// bluetoothAdapter.cancelDiscovery();
// }
}
private boolean checkCoarsePermission(){
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;
}
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED));
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(devicesFoundReceiver);
}
private final BroadcastReceiver devicesFoundReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action= intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);
String RSSI = String.valueOf(rssi);
Toast.makeText(context.getApplicationContext(),"rssi "+RSSI+" "+device.getAddress(),Toast.LENGTH_SHORT).show();
Log.d("Logger", "Recive data "+device.getAddress());
if(mqtt_server!=null||mqtt_id!=null||mqtt_port!=null){
try {
Log.d("Logger", "Sending data");
String payload = "rssi:"+RSSI+"mac:"+device.getAddress();
client.publish("test",payload.getBytes(),0,false);
} catch ( MqttException e) {
e.printStackTrace();
Log.d("Logger", "Error Sending "+e);
}}
}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
}else if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
}
}
};
#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(this,"ALLOWED", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this,"Forbidden",Toast.LENGTH_SHORT).show();
} break;
}
}
}
App Flow:
Insert MQTT server, port, id, topic.
Turn on the proccess.
Android scan BLE/Beacon
Android sending MAC/RSSI to MQTT
I hope someone can help to guide me, on how to make the application run in the background?
I'm a beginner, and I don't understand how to implement background service in my application. Please help me!
You need to implement a foreground service that will handle your ble scanning and MQTT logic.
See this article with an overview of how to do it. Depending on your build/target SDK, the implementation will vary.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 11 months ago.
Improve this question
Full code
public class MainActivity extends AppCompatActivity {
private Button btnTurn;
private boolean state = false;
private BluetoothAdapter bluetoothAdapter;
public static final int REQUEST_ACCESS_COARSE_LOCATION = 1;
public String mUrl = "http://192.168.6.56/server_http/api/show.php/";
public String data;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnTurn = findViewById(R.id.btnTurn);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
stateCheck();
Log.d("Logger", "State Check");
handler.postDelayed(this, 1000);
}
}, 1000);
btnTurn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(!state){
btnTurn.setText("Turn Off");
state = true;
}else{
btnTurn.setText("Turn On");
state = false;
}
}
});
}
public void stateCheck(){
if (state){
if (bluetoothAdapter!=null & bluetoothAdapter.isEnabled()) {
if(checkCoarsePermission()){
Log.d("Logger", "Discover");
bluetoothAdapter.startDiscovery();
}
}
}
}
private boolean checkCoarsePermission(){
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;
}
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED));
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(devicesFoundReceiver);
}
private final BroadcastReceiver devicesFoundReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action= intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);
String RSSI = String.valueOf(rssi);
Toast.makeText(context.getApplicationContext(),"rssi "+RSSI+" "+device.getAddress(),Toast.LENGTH_SHORT).show();
Log.d("Logger", "Recive data "+device.getAddress());
data = "RSSI: "+RSSI+" MAC: "+device.getAddress();
try{
GetText(mUrl,data);
}catch (IOException e){
Log.e("Logger","Error Function to send");
}
}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
}else if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
}
}
};
#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(this,"ALLOWED", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this,"Forbidden",Toast.LENGTH_SHORT).show();
} break;
}
}
public void post(String urll, String dataa) throws Exception {
HttpClient client = HttpClient.newBuilder().build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(urll))
.POST(BodyPublishers.ofString(dataa))
.build();
HttpResponse<?> response = client.send(request, BodyHandlers.discarding());
System.out.println(response.statusCode());
}
}
Problem of code
Error
I want send RSSI and MAC of Bluetooth scan using HTTP protocol, i used method HttpClient as plain java in Android Studio Java. But in my case, the code will error. I don't know why. I was included library and gradle in my android studio but still error. Anyone can help me how to solve it? Thanks
By the screenshots you posted, it looks like you're pointing to the classes of the package org.apache.http, but from the methods you use in your code, I think you rather want to use java.net.http classes.
So simply fix your import statements on top of your class, from:
import org.apache.http.HttpClient;
import org.apache.http.HttpResponse;
import org.apache.http.HttpRequest;
//etc.
to:
import java.net.http.HttpClient;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest;
//etc.
I'm creating my first android app, so I'm really a beginner with (android) development.
The first fase of the app is to implement an Activity (i.e. BleActivity.java), which is called when a certain button is clicked. The BleActivity should list available BLE devices (testing with a tiSensorTag CC2650) and later on I want to read the data from the devices.
It was quite a challenge to get it to work because most of the tutorials online are written with deprecated API's. After combining several tutorials, the app is now working!
There is one bug in the app which I'm unable to fix:
When bluetooth is turned off and I trigger the BleActivity, the onResume() checks if bluetooth is enabled (which is not the case) and if it's not, a dialog screen appears requesting the user to turn bluetooth on.
I can see the dialog screen but before I'm able to use it, the app crashes.
BleActivity.java
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
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.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class BleActivity extends ListActivity {
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
// see nested class LeDeviceListAdapter
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothAdapter mBluetoothAdapter;
private static final int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 10000;
private Handler mHandler;
private boolean mScanning;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private static final String INFO = "ZINFO";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check if BLE is supported
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
} else {
Log.i(INFO, "onCreate: BLE is supported");
}
// Create a Handler to send and process Message Class and Runnable objects associated with a thread's MessageQueue.
mHandler = new Handler();
// Get BluetoothManager and BluetoothAdapter in order to conduct overall Bluetooth Management.
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
#Override
protected void onResume() {
super.onResume();
// Initialize list view adapter
mLeDeviceListAdapter = new LeDeviceListAdapter();
setListAdapter(mLeDeviceListAdapter);
//Check if permission for ACCESS_COARSE_LOCATION (AndroidManifest.xml) is granted.
if (checkLocationPermission()) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, R.string.grant_permission, Toast.LENGTH_LONG).show();
finish();
} else {
Log.i(INFO, "onResume: Permission for ACCESS_COARSE_LOCATION is granted");
}
}
// Check if bluetoothAdapter is successfully obtained and if BLE is enabled.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
Log.i(INFO, "onResume: BLE is enabled");
}
// GET getBluetoothLeScanner(): This class provides methods to perform scan related operations for Bluetooth LE devices
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
// Set scan settings
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).build();
// Set device filter (null is allowed)
filters = new ArrayList<ScanFilter>();
// START SCAN FOR BLE DEVICES!
scanLeDevice(true);
}
// When user denies prompt for enabling Bluetooth, the Activity is closed
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
// Pause scanning for BLE devices */
#Override
protected void onPause() {
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
}
//Methods for permission check
public boolean checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
new AlertDialog.Builder(this)
.setTitle(R.string.title_location_permission)
.setMessage(R.string.text_location_permission)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityCompat.requestPermissions(BleActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
}
})
.create()
.show();
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission. ACCESS_COARSE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
}
return false;
} else {
return true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(INFO, "onRequestPermissionsResult: PERMISSION_GRANTED");
} else {
finish();
}
}
}
}
// Methods for START & STOP scan
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mLEScanner.stopScan(mScanCallback); //STOP SCANNING
}
}, SCAN_PERIOD); // Stops scanning after a pre-defined scan period.
mScanning = true;
mLEScanner.startScan(filters, settings, mScanCallback); //START SCANNING
} else {
mScanning = false;
mLEScanner.stopScan(mScanCallback); //STOP SCANNING
}
if(mScanning == true) {
//TODO: Implement code for when the app is scanning (green stoplight, turning wheel, etc.)
} else {
//TODO: Implement code for when the app is NOT scanning (red stoplight, idle wheel, etc.)
}
}
// Bluetooth LE scan results are reported using these callbacks.
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice btDevice = result.getDevice();
mLeDeviceListAdapter.addDevice(btDevice);
mLeDeviceListAdapter.notifyDataSetChanged(); }
#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);
}
};
// ListAdapter 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 = BleActivity.this.getLayoutInflater();
}
public void addDevice(BluetoothDevice device) {
if(!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
/*
TODO: implement onListItemClick (see example code)
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.activity_ble, 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;
}
}
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
}
}
Your help is much appreciated and please keep in mind that this is my first app and I'm still an amateur/beginner developer.
logcat:
08-06 16:21:39.152 5489-5489/nl.cargosys.iotcloudconnect E/AndroidRuntime: FATAL EXCEPTION: main
Process: nl.cargosys.iotcloudconnect, PID: 5489
java.lang.RuntimeException: Unable to pause activity {nl.cargosys.iotcloudconnect/nl.cargosys.iotcloudconnect.BleActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.le.BluetoothLeScanner.stopScan(android.bluetooth.le.ScanCallback)' on a null object reference
at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:4179)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4145)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4119)
at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:4093)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1654)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.le.BluetoothLeScanner.stopScan(android.bluetooth.le.ScanCallback)' on a null object reference
at nl.cargosys.iotcloudconnect.BleActivity.scanLeDevice(BleActivity.java:179)
at nl.cargosys.iotcloudconnect.BleActivity.onPause(BleActivity.java:121)
at android.app.Activity.performPause(Activity.java:7148)
at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1330)
at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:4168)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4145)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4119)
at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:4093)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1654)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
In your if condition below -
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())
The main problem lies in your -
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
// Set scan settings
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).build();
// Set device filter (null is allowed)
filters = new ArrayList<ScanFilter>();
// START SCAN FOR BLE DEVICES!
scanLeDevice(true);
You check if the adapter is available and if not you start another activity for result, but you continue to do the operation in your scanner code and scan for device after your else. Ideally you should place the above code in your else statement and only start scanning device if you are 100% sure that you have Bluetooth enabled.
Update 1
Ok so this error is coming from your onPause() method where you are calling scanLeDevice(false); method. Now inside this method you do mLEScanner.stopScan(mScanCallback); but your mLEScanner is not yet initialized as the initialization code is in your else statement or after it in the line
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
So it is better if you do this in your method-
if(mLEScanner!=null)
{
mLEScanner.stopScan(mScanCallback);
}
I've tried following the instructions on the spotify android sdk site and when I tested it on my emulator, all I got was a white screen. I understand the demo code is supposed to pull up a login request and then play a song track if the login was successful am I right?
Here's my code for reference
package com.example.wilsonler.spotifytest2;
import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.spotify.sdk.android.player.Spotify;
import com.spotify.sdk.android.authentication.AuthenticationClient;
import com.spotify.sdk.android.authentication.AuthenticationRequest;
import com.spotify.sdk.android.authentication.AuthenticationResponse;
import com.spotify.sdk.android.player.Config;
import com.spotify.sdk.android.player.ConnectionStateCallback;
import com.spotify.sdk.android.player.Player;
import com.spotify.sdk.android.player.PlayerNotificationCallback;
import com.spotify.sdk.android.player.PlayerState;
public class MainActivity extends Activity implements
PlayerNotificationCallback, ConnectionStateCallback {
// TODO: Replace with your client ID
private static final String CLIENT_ID = "3488191ba4e54c958f83e74b240c528c";
// TODO: Replace with your redirect URI
private static final String REDIRECT_URI = "http://localhost:1034://authenticationResponse";
// Request code that will be passed together with authentication result to the onAuthenticationResult callback
// Can be any integer
private static final int REQUEST_CODE = 1337;
private Player mPlayer;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AuthenticationRequest.Builder builder =
new AuthenticationRequest.Builder(CLIENT_ID, AuthenticationResponse.Type.TOKEN, REDIRECT_URI);
builder.setScopes(new String[]{"user-read-private", "streaming"});
AuthenticationRequest request = builder.build();
AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request);
}
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
// Check if result comes from the correct activity
if (requestCode == REQUEST_CODE) {
AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, intent);
if (response.getType() == AuthenticationResponse.Type.TOKEN) {
Config playerConfig = new Config(this, response.getAccessToken(), CLIENT_ID);
mPlayer = Spotify.getPlayer(playerConfig, this, new Player.InitializationObserver() {
#Override
public void onInitialized(Player player) {
mPlayer.addConnectionStateCallback(MainActivity.this);
mPlayer.addPlayerNotificationCallback(MainActivity.this);
mPlayer.play("spotify:track:2TpxZ7JUBn3uw46aR7qd6V");
}
#Override
public void onError(Throwable throwable) {
Log.e("MainActivity", "Could not initialize player: " + throwable.getMessage());
}
});
}
}
}
#Override
public void onLoggedIn() {
Log.d("MainActivity", "User logged in");
}
#Override
public void onLoggedOut() {
Log.d("MainActivity", "User logged out");
}
#Override
public void onLoginFailed(Throwable error) {
Log.d("MainActivity", "Login failed");
}
#Override
public void onTemporaryError() {
Log.d("MainActivity", "Temporary error occurred");
}
#Override
public void onConnectionMessage(String message) {
Log.d("MainActivity", "Received connection message: " + message);
}
#Override
public void onPlaybackEvent(EventType eventType, PlayerState playerState) {
Log.d("MainActivity", "Playback event received: " + eventType.name());
switch (eventType) {
// Handle event type as necessary
default:
break;
}
}
#Override
public void onPlaybackError(ErrorType errorType, String errorDetails) {
Log.d("MainActivity", "Playback error received: " + errorType.name());
switch (errorType) {
// Handle error type as necessary
default:
break;
}
}
#Override
protected void onDestroy() {
// VERY IMPORTANT! This must always be called or else you will leak resources
Spotify.destroyPlayer(this);
super.onDestroy();
}
}
I am trying to use Google's Nearby Messages API for the first time. I downloaded some source code from GitHub and tried to compile it using Android Studio. There is an import that Android Studio cannot resolve:
import com.google.android.gms.nearby.messages.SubscribeOptions;
My Gradle file defines the following dependencies:
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.2.0'
compile 'com.google.android.gms:play-services:7.8.0'
compile 'com.google.android.gms:play-services-nearby:7.8.0'
and this is the Activity in which I am using the aforementioned library:
package com.example.android.nearbybeacons;
import android.Manifest;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.nearby.Nearby;
import com.google.android.gms.nearby.messages.Message;
import com.google.android.gms.nearby.messages.MessageListener;
import com.google.android.gms.nearby.messages.Strategy;
import com.google.android.gms.nearby.messages.SubscribeOptions;
public class MainActivity extends AppCompatActivity implements
AdapterView.OnItemClickListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG =
MainActivity.class.getSimpleName();
private static final int REQUEST_RESOLVE_ERROR = 100;
private static final int REQUEST_PERMISSION = 42;
private GoogleApiClient mGoogleApiClient;
private ArrayAdapter<OfferBeacon> mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView list = (ListView) findViewById(R.id.list);
mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
list.setAdapter(mAdapter);
list.setOnItemClickListener(this);
//Construct a connection to Play Services
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Nearby.MESSAGES_API)
.build();
//When launching from a notification link
if (BeaconService.ACTION_DISMISS.equals(getIntent().getAction())) {
//Fire a clear action to the service
Intent intent = new Intent(this, BeaconService.class);
intent.setAction(BeaconService.ACTION_DISMISS);
startService(intent);
}
}
#Override
protected void onStart() {
super.onStart();
//Initiate connection to Play Services
mGoogleApiClient.connect();
//The location permission is required on API 23+ to obtain BLE scan results
int result = ActivityCompat
.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION);
if (result != PackageManager.PERMISSION_GRANTED) {
//Ask for the location permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_PERMISSION);
}
}
#Override
protected void onStop() {
super.onStop();
//Tear down Play Services connection
if (mGoogleApiClient.isConnected()) {
Log.d(TAG, "Un-subscribing…");
Nearby.Messages.unsubscribe(
mGoogleApiClient,
mMessageListener);
mAdapter.clear();
mGoogleApiClient.disconnect();
}
}
// This is called in response to a button tap in the system permissions dialog.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_RESOLVE_ERROR) {
if (resultCode == RESULT_OK) {
// Permission granted or error resolved successfully then we proceed
// with publish and subscribe..
subscribe();
} else {
// This may mean that user had rejected to grant nearby permission.
showToast("Failed to resolve error with code " + resultCode);
}
}
if (requestCode == REQUEST_PERMISSION) {
if (resultCode != RESULT_OK) {
showToast("We need location permission to get scan results!");
finish();
}
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
OfferBeacon item = mAdapter.getItem(position);
showToast(item.offer);
}
/* Nearby Messages Callbacks */
//NOTE: These callbacks are NOT triggered on the main thread!
private MessageListener mMessageListener = new MessageListener() {
// Called each time a new message is discovered nearby.
#Override
public void onFound(Message message) {
Log.i(TAG, "Found message: " + message);
final OfferBeacon beacon = new OfferBeacon(message);
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.add(beacon);
}
});
}
// Called when the publisher (beacon) is no longer nearby.
#Override
public void onLost(Message message) {
Log.i(TAG, "Lost message: " + message);
final OfferBeacon beacon = new OfferBeacon(message);
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.remove(beacon);
}
});
}
};
/* API Client Callbacks */
#Override
public void onConnected(Bundle bundle) {
//Once connected, we have to check that the user has opted in
Runnable runOnSuccess = new Runnable() {
#Override
public void run() {
//Subscribe once user permission is verified
subscribe();
}
};
ResultCallback<Status> callback =
new ErrorCheckingCallback(runOnSuccess);
Nearby.Messages.getPermissionStatus(mGoogleApiClient)
.setResultCallback(callback);
}
#Override
public void onConnectionSuspended(int i) {
Log.d(TAG, "OnConnectionSuspended");
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.w(TAG, "OnConnectionFailed");
}
private void subscribe() {
Log.d(TAG, "Subscribing…");
SubscribeOptions options = new SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.build();
//Active subscription for foreground messages
Nearby.Messages.subscribe(mGoogleApiClient,
mMessageListener, options);
//Passive subscription for background messages
Intent serviceIntent = new Intent(this, BeaconService.class);
PendingIntent trigger = PendingIntent.getService(this, 0,
serviceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
ResultCallback<Status> callback = new BackgroundRegisterCallback();
Nearby.Messages.subscribe(mGoogleApiClient, trigger, options)
.setResultCallback(callback);
}
private class BackgroundRegisterCallback implements ResultCallback<Status> {
#Override
public void onResult(#NonNull Status status) {
//Validate if we were able to register for background scans
if (status.isSuccess()) {
Log.d(TAG, "Background Register Success!");
} else {
Log.w(TAG, "Background Register Error ("
+ status.getStatusCode() + "): "
+ status.getStatusMessage());
}
}
}
//ResultCallback triggered when to handle Nearby permissions check
private class ErrorCheckingCallback implements ResultCallback<Status> {
private final Runnable runOnSuccess;
private ErrorCheckingCallback(#Nullable Runnable runOnSuccess) {
this.runOnSuccess = runOnSuccess;
}
#Override
public void onResult(#NonNull Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Permission status succeeded.");
if (runOnSuccess != null) {
runOnSuccess.run();
}
} else {
// Currently, the only resolvable error is that the device is not opted
// in to Nearby. Starting the resolution displays an opt-in dialog.
if (status.hasResolution()) {
try {
status.startResolutionForResult(MainActivity.this,
REQUEST_RESOLVE_ERROR);
} catch (IntentSender.SendIntentException e) {
showToastAndLog(Log.ERROR, "Request failed with exception: " + e);
}
} else {
showToastAndLog(Log.ERROR, "Request failed with : " + status);
}
}
}
}
private void showToast(String text) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
private void showToastAndLog(int logLevel, String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
Log.println(logLevel, TAG, message);
}
}
Can anyone tell me what I'm doing wrong?
My guess is that you will be able to resolve this by updating the Play Services version you are using from v7.8.0 to v8.4.0 (the latest version as of this answer date). The Nearby APIs were introduced in v7.8.0, at which time subscription could be accomplished using a method signature of the form
subscribe(GoogleApiClient, MessageListener, Strategy)
However, this method is now (in v8.4.0) marked as deprecated, and a new method with the following signature is recommended in its place:
subscribe (GoogleApiClient, MessageListener, SubscribeOptions)
Based on this information it seems fairly likely that the SubscribeOptions type was introduced whenever the previously-described deprecation took place, which was clearly post-v7.8.0.
See these docs for reference.