I am trying to do realtime bluetooth location tracking using Android. I want to scan for bluetooth le and upload to a server in real time even when the phone is locked.
When running the app however, after 30 minutes the network request queue stops being processed because of Android's built-in Doze Mode.
I've tried moving my background service to the foreground but to no avail.
Any ideas how I can do constant network in Doze Mode? This won't be a public app so won't be submitted to Google Play, it will only be installed to Enterprise devices.
My Service Class:
package REDACTED.beaconlistener;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
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.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.RejectedExecutionException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
public class BroadcastService extends Service {
private static final String TAG = "BROADCAST";
private static final String strURL = "REDACTED";
private long lastAlertTimestamp;
private ScanCallback callback;
private Timer timer;
private BluetoothLeScanner scanner;
private ScanSettings settings;
private PowerManager.WakeLock wakeLock;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle("My Notification")
.setContentText("My Message")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent)
.setTicker("TICKER TEXT")
.build();
startForeground(999, notification);
lastAlertTimestamp = Calendar.getInstance().getTime().getTime() / 1000;
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Log.e(TAG, "Bluetooth LE not supported!");return;
}
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
if (!bluetoothAdapter.isEnabled()) {
bluetoothAdapter.enable();
}
scanner = bluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
callback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
if (result.getDevice().getAddress().substring(0, 8).equals("0C:F3:EE")) {
// Its a pill!
String[] vcodeArr = result.getDevice().getAddress().substring(9).split(":");
String vcode = vcodeArr[2] + vcodeArr[1] + vcodeArr[0];
Log.d("SCANNER", vcode + " : " + String.valueOf(result.getRssi() + 128));
Long ts = System.currentTimeMillis()/1000;
PingUrlTask task = new PingUrlTask();
try {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, strURL, vcode, String.valueOf(result.getRssi() + 128), getUniquePsuedoID(), ts.toString());
} catch(RejectedExecutionException e) {
Log.e("BROADCAST", "RejectedExecutionException");
}
}
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
this.onScanResult(0, sr);
}
}
#Override
public void onScanFailed(int errorCode) {
// Do Nothing.
Log.e("SCANNER", String.valueOf(errorCode));
}
};
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
try {
new PingUrlTask().execute(strURL, null, String.valueOf(0), getUniquePsuedoID());
} catch(Exception e) {
Log.e("BROADCAST", e.getMessage());
e.printStackTrace();
}
}
}, 10000, 10000);
try {
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BeaconListener");
} catch(NullPointerException e) {
// Do nothing.
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ping("Service Started");
try {
wakeLock.acquire();
} catch(Exception e) {
// Do nothing.
}
// bluetoothAdapter.startLeScan(callback);
List<ScanFilter> filter = new ArrayList<ScanFilter>();
scanner.startScan(filter, settings, callback);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
try {
wakeLock.release();
} catch(Exception e) {
// Do nothing.
}
// bluetoothAdapter.stopLeScan(callback);
scanner.stopScan(callback);
timer.cancel();
ping("Service Stopped");
}
public String getUniquePsuedoID() {
// If all else fails, if the user does have lower than API 9 (lower
// than Gingerbread), has reset their device or 'Secure.ANDROID_ID'
// returns 'null', then simply the ID returned will be solely based
// off their Android device information. This is where the collisions
// can happen.
// Thanks http://www.pocketmagic.net/?p=1662!
// Try not to use DISPLAY, HOST or ID - these items could change.
// If there are collisions, there will be overlapping data
String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);
// Thanks to #Roman SL!
// https://stackoverflow.com/a/4789483/950427
// Only devices with API >= 9 have android.os.Build.SERIAL
// http://developer.android.com/reference/android/os/Build.html#SERIAL
// If a user upgrades software or roots their device, there will be a duplicate entry
String serial = null;
try {
serial = android.os.Build.class.getField("SERIAL").get(null).toString();
// Go ahead and return the serial for api => 9
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
// String needs to be initialized
serial = "serial"; // some value
}
// Thanks #Joe!
// https://stackoverflow.com/a/2853253/950427
// Finally, combine the values we have found by using the UUID class to create a unique identifier
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
private void ping(String text) {
Log.d("BROADCAST", text);
// new PingUrlTask().execute(strURL);
}
private void createPushNotification(String title, String text) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder b = new NotificationCompat.Builder(getApplicationContext());
b.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setTicker("Hearty365")
.setContentTitle(title)
.setContentText(text)
.setDefaults(Notification.DEFAULT_LIGHTS| Notification.DEFAULT_SOUND)
.setContentIntent(contentIntent)
.setContentInfo("Info")
.setPriority(Notification.PRIORITY_HIGH);
NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, b.build());
}
private class PingUrlTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(final String... strURL) {
Log.d("BROADCAST", "PingUrlTask");
Log.d("BROADCAST", strURL[0]);
try {
URL requestUrl = new URL(strURL[0]);
HttpsURLConnection conn = (HttpsURLConnection) requestUrl.openConnection();
conn.setRequestMethod("POST");
conn.setReadTimeout(95 * 1000);
conn.setConnectTimeout(95 * 1000);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("X-Environment", "android");
conn.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
conn.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());
if (strURL.length != 5) {
return null;
}
// Write Post data
StringBuilder queryParams = new StringBuilder();
queryParams.append("vcode=");
queryParams.append(strURL[1]);
queryParams.append("&rssi=");
queryParams.append(strURL[2]);
queryParams.append("&deviceId=");
queryParams.append(strURL[3]);
queryParams.append("×tamp=");
queryParams.append(strURL[4]);
queryParams.append("&time=");
queryParams.append(lastAlertTimestamp);
Log.d("BROADCAST", queryParams.toString());
DataOutputStream dStream = new DataOutputStream(conn.getOutputStream());
dStream.writeBytes(queryParams.toString());
conn.connect();
// Read response
Log.d(TAG, String.valueOf(conn.getResponseCode()));
if (conn.getResponseCode() == 200) {
StringBuilder result = new StringBuilder();
InputStream input = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line;
while((line = reader.readLine()) != null) {
result.append(line);
}
Log.d(TAG, result.toString());
try {
JSONObject obj = new JSONObject(result.toString());
lastAlertTimestamp = obj.getLong("serverTime");
JSONArray alerts = obj.getJSONArray("alerts");
for(int i = 0; i < alerts.length(); i++) {
JSONObject alert = alerts.getJSONObject(i);
createPushNotification(alert.getString("alert_title"), alert.getString("alert_message"));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
dStream.flush();
dStream.close();
Log.d("BROADCAST", String.valueOf(conn.getResponseCode()));
} catch(Exception e) {
Log.e("BROADCAST", e.getMessage());
e.printStackTrace();
}
// Handler handler = new Handler(Looper.getMainLooper());
// handler.post(new Runnable() {
// #Override
// public void run() {
// if (MainActivity.mWebView != null) {
// try {
// JSONObject payload = new JSONObject();
// payload.put("vcode", strURL[1]);
// payload.put("rssi", strURL[2]);
// payload.put("deviceId", strURL[3]);
// payload.put("timestamp", strURL[4]);
// payload.put("time", lastAlertTimestamp);
// String command = String.format("androidData(%s)", payload.toString());
//
// Log.d(TAG, command);
// MainActivity.mWebView.evaluateJavascript(command, null);
// } catch(Exception e) {
// Log.d(TAG, e.getMessage());
// e.printStackTrace();
// }
// } else {
// Log.w(TAG, "No WebView!");
// }
// }
// });
return null;
}
}
}
Related
I'm working on a project in which I want to receive the data(, separated value"only two values") from BLE server. my first question is I write the code for connection but I din't connect with it. and second how to read this comma separated value. and this comma separated value will come from arduino to BLE server and from BLE server the communication will be done between both. Code is Below.
IDs
#define SERVICE_UUID "ab0828b1-198e-4351-b779-901fa0e0371e"
#define CHARACTERISTIC_UUID_RX "4ac8a682-9736-4e5d-932b-e9b31405049c"
#define CHARACTERISTIC_UUID_TX "0972EF8C-7613-4075-AD52-756F33D4DA91"
code
package com.grng.rthr;
import androidx.appcompat.app.ActionBar;
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.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import com.grng.rthr.main.core.viewmodel.MainViewModel;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.grng.rthr.main.BackAwareApplication;
import com.grng.rthr.main.core.viewmodel.MainViewModel;
import com.grng.rthr.main.ui.fragment.atWork.ModeFragment;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.UUID;
public class BluetoothActivity extends AppCompatActivity {
// GUI Components
private TextView mBluetoothStatus;
private MainViewModel viewModel;
private TextView mReadBuffer;
private Button mScanBtn;
private Button mOffBtn;
private BluetoothAdapter mBTAdapter;
private Set<BluetoothDevice> mPairedDevices;
private ArrayAdapter<String> mBTArrayAdapter;
private ListView mDevicesListView;
private final String TAG = BluetoothActivity.class.getSimpleName();
private Handler mHandler; // Our main handler that will receive callback notifications
private ConnectedThread mConnectedThread; // bluetooth background worker thread to send and receive data
private BluetoothSocket mBTSocket = null; // bi-directional client-to-client data path
private static final UUID BTMODULEUUID = UUID.fromString("ab0828b1-198e-4351-b779-901fa0e0371e"); // "random" unique identifier
// #defines for identifying shared types between calling functions
private final static int REQUEST_ENABLE_BT = 1; // used to identify adding bluetooth names
private final static int MESSAGE_READ = 2; // used in bluetooth handler to identify message update
private final static int CONNECTING_STATUS = 3; // used in bluetooth handler to identify message status
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth);
// setTitle("Belt Monitoring");
setTitle("Belt Monitoring");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_back_btn);
mBluetoothStatus = (TextView)findViewById(R.id.bluetoothStatus);
mReadBuffer = (TextView) findViewById(R.id.readBuffer);
mScanBtn = (Button)findViewById(R.id.scan);
mOffBtn = (Button)findViewById(R.id.off);
mBTArrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
mBTAdapter = BluetoothAdapter.getDefaultAdapter(); // get a handle on the bluetooth radio
mDevicesListView = (ListView)findViewById(R.id.devicesListView);
mDevicesListView.setAdapter(mBTArrayAdapter); // assign model to view
mDevicesListView.setOnItemClickListener(mDeviceClickListener);
bluetoothOn();
// Ask for location permission if not already allowed
if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
mHandler = new Handler(){
public void handleMessage(android.os.Message msg){
if(msg.what == MESSAGE_READ){
String readMessage = null;
try {
readMessage = new String((byte[]) msg.obj, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
mReadBuffer.setText(readMessage);
}
if(msg.what == CONNECTING_STATUS){
if(msg.arg1 == 1)
mBluetoothStatus.setText("Connected to Device: " + (String)(msg.obj));
else
mBluetoothStatus.setText("Connection Failed");
}
}
};
if (mBTArrayAdapter == null) {
// Device does not support Bluetooth
mBluetoothStatus.setText("Status: Bluetooth not found");
Toast.makeText(getApplicationContext(),"Bluetooth device not found!",Toast.LENGTH_SHORT).show();
}
else {
mScanBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bluetoothScan(v);
}
});
mOffBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
bluetoothOff(v);
}
});
}
}
private void bluetoothOn(){
if (!mBTAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
mBluetoothStatus.setText("Bluetooth enabled");
Toast.makeText(getApplicationContext(),"Bluetooth turned on",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(getApplicationContext(),"Bluetooth is already on", Toast.LENGTH_SHORT).show();
}
}
private void bluetoothScan(View view){
mBTArrayAdapter.clear();
mPairedDevices = mBTAdapter.getBondedDevices();
if(mBTAdapter.isEnabled()) {
// put it's one to the adapter
for (BluetoothDevice device : mPairedDevices)
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
Toast.makeText(getApplicationContext(), "Show Paired Devices", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
}
discovering();
}
private void discovering(){
if(mBTAdapter.isDiscovering()){
mBTAdapter.cancelDiscovery();
Toast.makeText(getApplicationContext(),"Discovery stopped",Toast.LENGTH_SHORT).show();
}
else{
if(mBTAdapter.isEnabled()) {
mBTArrayAdapter.clear(); // clear items
mBTAdapter.startDiscovery();
Toast.makeText(getApplicationContext(), "Discovery started", Toast.LENGTH_SHORT).show();
registerReceiver(blReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
}
else{
Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
}
}
}
// Enter here after user selects "yes" or "no" to enabling radio
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent Data){
// Check which request we're responding to
if (requestCode == REQUEST_ENABLE_BT) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// The user picked a contact.
// The Intent's data Uri identifies which contact was selected.
mBluetoothStatus.setText("Enabled");
}
else
mBluetoothStatus.setText("Disabled");
}
}
private void bluetoothOff(View view){
mBTAdapter.disable(); // turn off
mBluetoothStatus.setText("Bluetooth disabled");
Toast.makeText(getApplicationContext(),"Bluetooth turned Off", Toast.LENGTH_SHORT).show();
}
final BroadcastReceiver blReceiver = 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);
// add the name to the list
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
mBTArrayAdapter.notifyDataSetChanged();
}
}
};
private AdapterView.OnItemClickListener mDeviceClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
if(!mBTAdapter.isEnabled()) {
Toast.makeText(getBaseContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
return;
}
mBluetoothStatus.setText("Connecting...");
// Get the device MAC address, which is the last 17 chars in the View
String info = ((TextView) v).getText().toString();
final String address = info.substring(info.length() - 17);
final String name = info.substring(0,info.length() - 17);
// Spawn a new thread to avoid blocking the GUI one
new Thread()
{
public void run() {
boolean fail = false;
BluetoothDevice device = mBTAdapter.getRemoteDevice(address);
try {
mBTSocket = createBluetoothSocket(device);
} catch (IOException e) {
fail = true;
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
// Establish the Bluetooth socket connection.
try {
mBTSocket.connect();
} catch (IOException e) {
try {
fail = true;
mBTSocket.close();
mHandler.obtainMessage(CONNECTING_STATUS, -1, -1)
.sendToTarget();
} catch (IOException e2) {
//insert code to deal with this
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
}
if(fail == false) {
mConnectedThread = new ConnectedThread(mBTSocket);
mConnectedThread.start();
mHandler.obtainMessage(CONNECTING_STATUS, 1, -1, name)
.sendToTarget();
}
}
}.start();
}
};
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", UUID.class);
return (BluetoothSocket) m.invoke(device, BTMODULEUUID);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
return device.createRfcommSocketToServiceRecord(BTMODULEUUID);
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.available();
if(bytes != 0) {
buffer = new byte[1024];
SystemClock.sleep(100); //pause and wait for rest of data. Adjust this depending on your sending speed.
bytes = mmInStream.available(); // how many bytes are ready to be read?
bytes = mmInStream.read(buffer, 0, bytes); // record how many bytes we actually read
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget(); // Send the obtained bytes to the UI activity
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(String input) {
byte[] bytes = input.getBytes(); //converts entered String into bytes
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
}
I am working with some application which starts a service.
When I close app under Android 7.0.0 its service is continue working fine.
But under Android 6.0.0 it does not.
I use this snippet to keep working service
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
What do I missing here?
Code of the service class
import android.Manifest;
//import android.app.LoaderManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
//import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
//import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
//import android.support.v4.content.ContextCompat;
//import android.telephony.TelephonyManager;
import android.text.format.DateFormat;
import android.util.Log;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.gson.Gson;
import java.util.Date;
import java.util.Timer;
//import java.util.TimerTask;
import java.io.*;
import java.net.*;
//import java.util.Timer;
//import java.util.TimerTask;
import static com.google.android.gms.location.LocationServices.getFusedLocationProviderClient;
public class gps_service extends Service {
private static final String TAG = "MyService";
private LocationListener listener;
private LocationManager locationManager;
private Timer timer = new Timer();
// private DLocation dLocation;
private final Object lock = new Object();
Context context;
private LocationRequest mLocationRequest;
private long UPDATE_INTERVAL = 60 * 1000; /* 60 secs */
private long FASTEST_INTERVAL = 10000; /* 10 sec */
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
startLocationUpdates();
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
context = this;
}
// Trigger new location updates at interval
protected void startLocationUpdates() {
// Create the location request to start receiving updates
mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
// Create LocationSettingsRequest object using location request
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// Check whether location settings are satisfied
// https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
settingsClient.checkLocationSettings(locationSettingsRequest);
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
getFusedLocationProviderClient(this).requestLocationUpdates(mLocationRequest, new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
// do work here
onLocationChanged(locationResult.getLastLocation());
}
}, Looper.myLooper());
}
public void getLastLocation() {
// Get last known recent location using new Google Play Services SDK (v11+)
FusedLocationProviderClient locationClient = getFusedLocationProviderClient(this);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
locationClient.getLastLocation()
.addOnSuccessListener(new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// GPS location can be null if GPS is switched off
if (location != null) {
onLocationChanged(location);
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Ошибка получения последнего GPS позиции");
e.printStackTrace();
}
});
}
public void onLocationChanged(Location location) {
// New location has now been determined
try
{
ComplexPreferences complexPreferences = ComplexPreferences.getComplexPreferences(context, "App_Settings", 0);
AppSettings appSettings = complexPreferences.getObject("App_Settings", AppSettings.class);
if (appSettings != null) {
LocationItem locationItem = new LocationItem();
locationItem.DeviceID = appSettings.getDeviceID();
locationItem.Latitude = Double.toString(location.getLatitude());
locationItem.Longitude = Double.toString(location.getLongitude());
Date d = new Date();
CharSequence timeOfRequest = DateFormat.format("yyyy-MM-dd HH:mm:ss", d.getTime()); // YYYY-MM-DD HH:mm:ss
locationItem.TimeOfRequest = timeOfRequest.toString();
locationItem.SerialNumber = appSettings.getSerialNumber();
//**************** Отправка сообщения в окно *********************
Intent i = new Intent("location_update");
Log.d(TAG, "Рассылка по слушателям в окнах");
DLocation dLocation = new DLocation();
dLocation.Latitude = locationItem.Latitude;
dLocation.Longitude = locationItem.Longitude;
dLocation.TimeOfRequest = locationItem.TimeOfRequest;
dLocation.Errors = "Данные передаются...";
i.putExtra("coordinates", dLocation);
sendBroadcast(i);
//**************** Отправка сообщения в окно *********************
Gson gson = new Gson();
String requestObject = gson.toJson(locationItem);
Log.d(TAG, "Формирование URL API сервера");
String url = appSettings.getIpAddress() + "/api/staff/savedata";
makeRequest(url, requestObject, dLocation);
}
}
catch (Exception ex)
{
Log.d(TAG, "Ошибка: " + ex.getMessage());
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (locationManager != null) {
locationManager.removeUpdates(listener);
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
public void makeRequest(String uri, String json, DLocation dLocation) {
HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
Handler mainHandler = new Handler(handlerThread.getLooper());
Runnable myRunnable = createRunnable(uri, json, dLocation);
mainHandler.post(myRunnable);
}
private Runnable createRunnable(final String uri, final String data,final DLocation dLocation){
Runnable aRunnable = new Runnable(){
public void run(){
try {
Log.d(TAG, "Перед запросом HTTP");
//Connect
HttpURLConnection urlConnection;
urlConnection = (HttpURLConnection) ((new URL(uri).openConnection()));
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestMethod("POST");
urlConnection.connect();
//Write
OutputStream outputStream = urlConnection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
try {
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG,"Ошибка записи в буфер для пережачи по HTTP");
}
writer.close();
outputStream.close();
//Read
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
bufferedReader.close();
String result = sb.toString();
Log.d(TAG, "После запроса HTTP");
Log.d(TAG, result);
// Log.d(TAG, "Результат передачи: " + result);
//**************** Отправка сообщения в окно *********************
Intent iResult = new Intent("location_update");
Log.d(TAG, "Рассылка по слушателям в окнах");
DLocation dLocation = new DLocation();
if(result.equals("true"))
{
dLocation.Errors = "Данные успешно переданы.";
}
else
{
dLocation.Errors = "Ошибка передачи данных.";
}
iResult.putExtra("result", dLocation);
sendBroadcast(iResult);
//**************** Отправка сообщения в окно *********************
}catch( Exception err){
err.printStackTrace();
Log.d(TAG, "Ошибка HTTP " + err.getMessage());
}
}
};
return aRunnable;
}
}
Use attribute android:stopWithTask="false" in manifest, if its true (By default its false if you are not using this in manifest).
<service
android:name=".gps_service"
android:enabled="true"
android:stopWithTask="false"/>
restart the service on onTaskRemoved().
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartService = new Intent(getApplicationContext(),
this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartServicePI);
}
As documentation says
onTaskRemoved will called if the service is currently running and the user has removed a task that comes from the service's application. If you have set ServiceInfo.FLAG_STOP_WITH_TASK then you will not receive this callback; instead, the service will simply be stopped.
So I have been working with arduino for a while and now I've been trying to connect to the blue-tooth module with android studio. I was able to get the blue-tooth to connect but I'm having issues making the blue-tooth connection a service so that it can run even if the app is closed. Whenever I try to get the service to run on my phone, the app crashes as soon as i try to connect to blue-tooth module where as before when it was an activity, it worked perfectly. I've looked at a lot of links and guides on using services and blue-tooth but none of them do exactly what I want.
Below are some code snippets, but here is a link to the entire GitHub project
Blue-tooth activity that I got to work:
package edu.memphis.teamhack.smart_nightlight;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
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.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.UUID;
public class BluetoothActivity extends AppCompatActivity {
// GUI Components
private TextView mBluetoothStatus;
private TextView mReadBuffer;
private Button mScanBtn;
private Button mOffBtn;
private Button mListPairedDevicesBtn;
private Button mDiscoverBtn;
private BluetoothAdapter mBTAdapter;
private Set<BluetoothDevice> mPairedDevices;
private ArrayAdapter<String> mBTArrayAdapter;
private ListView mDevicesListView;
private CheckBox mLED1;
private final String TAG = MainActivity.class.getSimpleName();
private Handler mHandler; // Our main handler that will receive callback notifications
private ConnectedThread mConnectedThread; // bluetooth background worker thread to send and receive data
private BluetoothSocket mBTSocket = null; // bi-directional client-to-client data path
private static final UUID BTMODULEUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // "random" unique identifier
// #defines for identifying shared types between calling functions
private final static int REQUEST_ENABLE_BT = 1; // used to identify adding bluetooth names
private final static int MESSAGE_READ = 2; // used in bluetooth handler to identify message update
private final static int CONNECTING_STATUS = 3; // used in bluetooth handler to identify message status
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth);
mBluetoothStatus = (TextView)findViewById(R.id.bluetoothStatus);
mReadBuffer = (TextView) findViewById(R.id.readBuffer);
mScanBtn = (Button)findViewById(R.id.scan);
mOffBtn = (Button)findViewById(R.id.off);
mDiscoverBtn = (Button)findViewById(R.id.discover);
mListPairedDevicesBtn = (Button)findViewById(R.id.PairedBtn);
mLED1 = (CheckBox)findViewById(R.id.checkboxLED1);
mBTArrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
mBTAdapter = BluetoothAdapter.getDefaultAdapter(); // get a handle on the bluetooth radio
mDevicesListView = (ListView)findViewById(R.id.devicesListView);
mDevicesListView.setAdapter(mBTArrayAdapter); // assign model to view
mDevicesListView.setOnItemClickListener(mDeviceClickListener);
// Ask for location permission if not already allowed
if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
mHandler = new Handler(){
public void handleMessage(android.os.Message msg){
if(msg.what == MESSAGE_READ){
String readMessage = null;
try {
readMessage = new String((byte[]) msg.obj, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
mReadBuffer.setText(readMessage);
}
if(msg.what == CONNECTING_STATUS){
if(msg.arg1 == 1)
mBluetoothStatus.setText("Connected to Device: " + (String)(msg.obj));
else
mBluetoothStatus.setText("Connection Failed");
}
}
};
if (mBTArrayAdapter == null) {
// Device does not support Bluetooth
mBluetoothStatus.setText("Status: Bluetooth not found");
Toast.makeText(getApplicationContext(),"Bluetooth device not found!",Toast.LENGTH_SHORT).show();
}
else {
mLED1.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
SharedPreferences settings = getSharedPreferences("MyPref",
Context.MODE_PRIVATE);
Integer myInt = settings.getInt("colorHex", 0);
if(mConnectedThread != null){ //First check to make sure thread created
mConnectedThread.write(myInt.toString());}
}
});
mScanBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bluetoothOn(v);
}
});
mOffBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
bluetoothOff(v);
}
});
mListPairedDevicesBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
listPairedDevices(v);
}
});
mDiscoverBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
discover(v);
}
});
}
}
private void bluetoothOn(View view){
if (!mBTAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
mBluetoothStatus.setText("Bluetooth enabled");
Toast.makeText(getApplicationContext(),"Bluetooth turned on",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(getApplicationContext(),"Bluetooth is already on", Toast.LENGTH_SHORT).show();
}
}
// Enter here after user selects "yes" or "no" to enabling radio
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent Data){
// Check which request we're responding to
if (requestCode == REQUEST_ENABLE_BT) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// The user picked a contact.
// The Intent's data Uri identifies which contact was selected.
mBluetoothStatus.setText("Enabled");
}
else
mBluetoothStatus.setText("Disabled");
}
}
private void bluetoothOff(View view){
mBTAdapter.disable(); // turn off
mBluetoothStatus.setText("Bluetooth disabled");
Toast.makeText(getApplicationContext(),"Bluetooth turned Off", Toast.LENGTH_SHORT).show();
}
private void discover(View view){
// Check if the device is already discovering
if(mBTAdapter.isDiscovering()){
mBTAdapter.cancelDiscovery();
Toast.makeText(getApplicationContext(),"Discovery stopped",Toast.LENGTH_SHORT).show();
}
else{
if(mBTAdapter.isEnabled()) {
mBTArrayAdapter.clear(); // clear items
mBTAdapter.startDiscovery();
Toast.makeText(getApplicationContext(), "Discovery started", Toast.LENGTH_SHORT).show();
registerReceiver(blReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
}
else{
Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
}
}
}
final BroadcastReceiver blReceiver = 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);
// add the name to the list
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
mBTArrayAdapter.notifyDataSetChanged();
}
}
};
private void listPairedDevices(View view){
mPairedDevices = mBTAdapter.getBondedDevices();
if(mBTAdapter.isEnabled()) {
// put it's one to the adapter
for (BluetoothDevice device : mPairedDevices)
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
Toast.makeText(getApplicationContext(), "Show Paired Devices", Toast.LENGTH_SHORT).show();
}
else
Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
}
private AdapterView.OnItemClickListener mDeviceClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
if(!mBTAdapter.isEnabled()) {
Toast.makeText(getBaseContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
return;
}
mBluetoothStatus.setText("Connecting...");
// Get the device MAC address, which is the last 17 chars in the View
String info = ((TextView) v).getText().toString();
final String address = info.substring(info.length() - 17);
final String name = info.substring(0,info.length() - 17);
// Spawn a new thread to avoid blocking the GUI one
new Thread()
{
public void run() {
boolean fail = false;
BluetoothDevice device = mBTAdapter.getRemoteDevice(address);
try {
mBTSocket = createBluetoothSocket(device);
} catch (IOException e) {
fail = true;
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
// Establish the Bluetooth socket connection.
try {
mBTSocket.connect();
} catch (IOException e) {
try {
fail = true;
mBTSocket.close();
mHandler.obtainMessage(CONNECTING_STATUS, -1, -1)
.sendToTarget();
} catch (IOException e2) {
//insert code to deal with this
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
}
if(fail == false) {
mConnectedThread = new ConnectedThread(mBTSocket);
mConnectedThread.start();
mHandler.obtainMessage(CONNECTING_STATUS, 1, -1, name)
.sendToTarget();
}
}
}.start();
}
};
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", UUID.class);
return (BluetoothSocket) m.invoke(device, BTMODULEUUID);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
return device.createRfcommSocketToServiceRecord(BTMODULEUUID);
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.available();
if(bytes != 0) {
buffer = new byte[1024];
SystemClock.sleep(100); //pause and wait for rest of data. Adjust this depending on your sending speed.
bytes = mmInStream.available(); // how many bytes are ready to be read?
bytes = mmInStream.read(buffer, 0, bytes); // record how many bytes we actually read
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget(); // Send the obtained bytes to the UI activity
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(String input) {
byte[] bytes = input.getBytes(); //converts entered String into bytes
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
Here is the Bluetooth Service I'm working on:
package edu.memphis.teamhack.smart_nightlight;
import android.app.Service;
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.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
public class BluetoothCommService extends Service {
// #defines for identifying shared types between calling functions
private final static int REQUEST_ENABLE_BT = 1; // used to identify adding bluetooth names
private BluetoothAdapter mBTAdapter;
private ArrayAdapter<String> mBTArrayAdapter;
public BluetoothCommService() {
}
public void onCreate(Bundle savedInstanceState){
//super.onCreate(savedInstanceState);
mBTAdapter = BluetoothAdapter.getDefaultAdapter(); // get a handle on the bluetooth radio
mBTArrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
void bluetoothOn(View view){
if (!mBTAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
//GUI
//mBluetoothStatus.setText("Bluetooth enabled");
// Toast.makeText(getApplicationContext(),"Bluetooth turned on",Toast.LENGTH_SHORT).show();
}
else{
//Toast.makeText(getApplicationContext(),"Bluetooth is already on", Toast.LENGTH_SHORT).show();
}
}
private void bluetoothOff(View view){
mBTAdapter.disable(); // turn off
//GUI
//mBluetoothStatus.setText("Bluetooth disabled");
//Toast.makeText(getApplicationContext(),"Bluetooth turned Off", Toast.LENGTH_SHORT).show();
}
private void discover(View view){
// Check if the device is already discovering
if(mBTAdapter.isDiscovering()){
mBTAdapter.cancelDiscovery();
Toast.makeText(getApplicationContext(),"Discovery stopped",Toast.LENGTH_SHORT).show();
}
else{
if(mBTAdapter.isEnabled()) {
mBTArrayAdapter.clear(); // clear items
mBTAdapter.startDiscovery();
Toast.makeText(getApplicationContext(), "Discovery started", Toast.LENGTH_SHORT).show();
registerReceiver(blReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
}
else{
Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
}
}
}
final BroadcastReceiver blReceiver = 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);
// add the name to the list
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
mBTArrayAdapter.notifyDataSetChanged();
}
}
};
}
Feel free to ask any questions.
Try this instructables, i did it by reffering to this tutorial
http://www.instructables.com/id/Android-Bluetooth-Control-LED-Part-2/
for more idea, also refer to
https://www.intorobotics.com/how-to-develop-simple-bluetooth-android-application-to-control-a-robot-remote/
I have a query regarding android push notification and i had asked it in another stackoverflow post and i did not get much help out of it [Query regarding Android push notifications. So i am posting it again, and it is as follows:
I have an android app that receives push notifications from Google push notification service. When i tap on the received notification, it opens an UI which displays this message, it is a list view. Now, when the user receives the push notification, and assuming that this screen is open, the UI should be refreshed automatically, such that it displays the latest notification. Could anybody let me know how i can solve this?
Below is my code that i have implemented:
Java code to receive the notification:
import java.util.Timer;
import java.util.TimerTask;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;
import com.example.foodu.R;
import com.google.android.gcm.GCMBaseIntentService;
public class GCMIntentService extends GCMBaseIntentService {
private static final String TAG = "GCM ::Service";
// Use your PROJECT ID from Google API into SENDER_ID
public static final String SENDER_ID = "53340195486";
public GCMIntentService() {
super(SENDER_ID);
}
#Override
protected void onError(Context arg0, String errorId) {
Log.e(TAG, "onError: errorId=" + errorId);
}
#Override
protected void onMessage(Context context, Intent data) {
String message;
// Message from PHP server
message = data.getStringExtra("message");
// Open a new activity called GCMMessageView
Intent intent = new Intent(this, com.example.foodu.Notification.class);
// Pass data to the new activity
intent.putExtra("message", message);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Starts the activity on notification click
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// Create the notification with a notification builder
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_logo)
.setWhen(System.currentTimeMillis())
.setContentTitle("Deals")
.setContentText(message).setContentIntent(pIntent)
.getNotification();
// Remove the notification on click
notification.flags |= Notification.FLAG_AUTO_CANCEL;
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(R.string.app_name, notification);
{
// Wake Android Device when notification received
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
final PowerManager.WakeLock mWakelock = pm.newWakeLock(
PowerManager.FULL_WAKE_LOCK
| PowerManager.ACQUIRE_CAUSES_WAKEUP, "GCM_PUSH");
mWakelock.acquire();
// Timer before putting Android Device to sleep mode.
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
mWakelock.release();
}
};
timer.schedule(task, 5000);
}
}
#Override
protected void onRegistered(Context arg0, String registrationId) {
Log.i(TAG, "onRegistered: registrationId=" + registrationId);
}
#Override
protected void onUnregistered(Context arg0, String registrationId) {
Log.i(TAG, "onUnregistered: registrationId=" + registrationId);
}
}
The code for the corresponding activity that would be launched when the user taps on the notification:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TimeZone;
import com.example.foodu.R;
import com.example.foodu.R.drawable;
import com.example.foodu.R.id;
import com.example.foodu.R.layout;
import com.example.foodu.R.menu;
import com.google.android.gcm.GCMRegistrar;
import android.support.v7.app.ActionBarActivity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.util.Log;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class Notification extends ActionBarActivity {
LinkedList<NotificationData> notificationList = new LinkedList<NotificationData>();
ListView listView = null;
NotificationListAdapter adaptor;
ActionMode mActionMode;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification);
overridePendingTransition(R.anim.trans_left_in, R.anim.trans_left_out);
listView = (ListView) findViewById(R.id.listView1);
// Retrive the data from GCMIntentService.java
Intent i = getIntent();
String message = i.getStringExtra("message");
//getDataForDisplay();
if(message!=null)
{
parseData(message);
}else{
getDataToDisplay();
}
adaptor = new NotificationListAdapter(getApplicationContext(), notificationList);
listView.setAdapter(adaptor);
TextView emptyText = (TextView) findViewById(R.id.empty);
emptyText.setText("No Events Yet!");
listView.setEmptyView(emptyText);
listView.setOnItemLongClickListener(new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
onListitemSelect(position);
view.setSelected(true);
return true;
}
});
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
adaptor.notifyDataSetChanged();
}
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
}
void writeToFile(){
FileOutputStream fos;
try {
fos = openFileOutput("varun", Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(notificationList);
oos.close();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void readFromFile(){
try{
FileInputStream fis = openFileInput("varun");
ObjectInputStream ois = new ObjectInputStream(fis);
LinkedList<NotificationData> local = (LinkedList<NotificationData>) ois.readObject();
ois.close();
for (int i = 0; i < local.size(); i++) {
notificationList.add(local.get(i));
}
}catch(Exception e){
e.printStackTrace();
}
}
private void getDataToDisplay() {
// TODO Auto-generated method stub
readFromFile();
}
private void parseData(String message) {
try {
int len = 0;
String[] stringArr = new String[100];
StringTokenizer st = new StringTokenizer(message, ".");
len = st.countTokens();
for (int i = 0; i < len; i++) {
if (st.hasMoreTokens()) {
stringArr[i] = st.nextToken();
}
}
NotificationData data = new NotificationData();
data.title = stringArr[0];
data.venue = stringArr[1];
data.date = stringArr[2];
data.time = stringArr[3];
notificationList.add(data);
readFromFile();
} catch (Exception e) {
e.printStackTrace();
}
}
private void getDateToDisplay() {
// TODO Auto-generated method stub
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
writeToFile();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.notificationmenu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
if(id == R.id.action_register){
registerDevice();
return true;
}
return super.onOptionsItemSelected(item);
}
private void registerDevice() {
try {
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
GCMRegistrar
.register(Notification.this, GCMIntentService.SENDER_ID);
} catch (Exception e) {
e.printStackTrace();
}
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.notificationcontext, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_calender:
addToCalender();
mode.finish();
return true;
case R.id.menu_delete:
//deleteData();
showAlertBox();
return false;
case R.id.menu_share:
shareDate();
mode.finish();
return true;
case R.id.menu_copy:
copyToClip();
mode.finish();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
adaptor.removeSelection();
}
};
void onListitemSelect(int position) {
adaptor.toggleSelection(position);
boolean hasCheckedItems = adaptor.getSelectedCount() > 0;
if (hasCheckedItems && mActionMode == null) {
mActionMode = startActionMode((Callback) mActionModeCallback);
} else if (!hasCheckedItems && mActionMode != null) {
mActionMode.finish();
}
if (mActionMode != null)
mActionMode.setTitle(String.valueOf(adaptor.getSelectedCount()));
}
protected void showAlertBox() {
// TODO Auto-generated method stub
AlertDialog.Builder builder1 = new AlertDialog.Builder(Notification.this);
builder1.setMessage("Delete " + adaptor.getSelectedIds().size()+ " events?");
builder1.setCancelable(true);
builder1.setIcon(R.drawable.alert);
builder1.setTitle("Caution");
builder1.setIcon(android.R.drawable.ic_dialog_alert);
builder1.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteData();
mActionMode.finish();
}
});
builder1.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert11 = builder1.create();
alert11.show();
}
protected void copyToClip() {
StringBuilder shareText = new StringBuilder();
for (int i = 0; i < adaptor.getSelectedIds().size(); i++) {
NotificationData data = notificationList
.get(adaptor.getSelectedIds().keyAt(i));
shareText.append(data.title + " " + data.venue + " " + data.date
+ " " + data.time);
shareText.append("\n");
}
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Notification App", shareText);
clipboard.setPrimaryClip(clip);
Toast.makeText(getApplicationContext(), "Data copied to ClipBoard",
Toast.LENGTH_LONG).show();
}
protected void shareDate() {
StringBuilder shareText = new StringBuilder();
for (int i = 0; i < adaptor.getSelectedIds().size(); i++) {
NotificationData data = notificationList
.get(adaptor.getSelectedIds().keyAt(i));
shareText.append(data.title + " " + data.venue + " " + data.date
+ " " + data.time);
shareText.append("\n");
}
String share = shareText.toString();
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, share);
sendIntent.setType("text/plain");
startActivity(sendIntent);
}
protected void deleteData() {
int count = 0;
int startPoint = adaptor.getSelectedIds().keyAt(0);
for (int i = 0; i < adaptor.getSelectedIds().size(); i++) {
adaptor.remove(notificationList.get(startPoint));
count++;
}
String message = " Event";
if(count>1)
{
message = " Events";
}
Toast.makeText(getApplicationContext(),
count + message+" deleted", Toast.LENGTH_LONG)
.show();
}
private void addToCalender() {
try {
int count = 0;
for (int i = 0; i < adaptor.getSelectedIds().size(); i++) {
NotificationData data = notificationList
.get(adaptor.getSelectedIds().keyAt(i));
ContentResolver cr = getApplicationContext()
.getContentResolver();
ContentValues values = new ContentValues();
String myDate = data.date + " " + data.time;
String timeArr[] = data.time.split("to");
SimpleDateFormat sfd = new SimpleDateFormat(
"' Date: 'MM/dd/yyyy 'Time: 'hh a", Locale.getDefault());
long time = sfd.parse(myDate).getTime();
values.put(CalendarContract.Events.DTSTART, time);
if (timeArr.length > 0) {
String endTime = timeArr[1];
SimpleDateFormat timeFormat = new SimpleDateFormat(
"' Date: 'MM/dd/yyyy hh a", Locale.getDefault());
long endtime = timeFormat.parse(data.date + " " + endTime)
.getTime();
values.put(CalendarContract.Events.DTEND, endtime);
}
values.put(CalendarContract.Events.TITLE, data.title);
values.put(CalendarContract.Events.DESCRIPTION, data.venue);
TimeZone timeZone = TimeZone.getDefault();
values.put(CalendarContract.Events.EVENT_TIMEZONE,
timeZone.getID());
values.put(CalendarContract.Events.CALENDAR_ID, 1);
Uri uri = cr
.insert(CalendarContract.Events.CONTENT_URI, values);
count++;
}
String message = " Event";
if(count>1)
{
message = " Events";
}
Toast.makeText(getApplicationContext(),
count + message + " added to Calender", Toast.LENGTH_LONG)
.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Use LocalBroadcastManager
Check following code / steps
1) Add in your activity (UI refresh Activity)
private BroadcastReceiver mMyBroadcastReceiver;
Then ,
2) In onResume
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mMyBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent)
{
// Here you can refresh your listview or other UI
Toast.makeText(getApplicationContext(), "Receiver", 2000).show();
}
};
try {
LocalBroadcastManager.getInstance(this).registerReceiver(mMyBroadcastReceiver,new IntentFilter("your_action"));
} catch (Exception e)
{
// TODO: handle exception
e.printStackTrace();
}}
// and your other code
3) Then unregister in onPause
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMyBroadcastReceiver);
}
4) Finally add in your GCM reciver class.
first check your activity is Visible or not using static variable
if visible add
Intent gcm_rec = new Intent("your_action"); LocalBroadcastManager.getInstance(arg0).sendBroadcast(gcm_rec);
else
use Notification Manager for notification.
I think this is easy and best way to refresh your listview UI / call Fetching method.
I am new to Java and I want to know how to invert audio (fixed voice inversion) coming from the input through a loopback to the output after a delay. How and where would I do this, and would I be able to set a frequency of the inversion somehow. Thanks.
package net.bitplane.android.microphone;
import java.nio.ByteBuffer;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.IBinder;
import android.util.Log;
public class MicrophoneService extends Service implements OnSharedPreferenceChangeListener {
private static final String APP_TAG = "Microphone";
private static final int mSampleRate = 44100;
private static final int mFormat = AudioFormat.ENCODING_PCM_16BIT;
private AudioTrack mAudioOutput;
private AudioRecord mAudioInput;
private int mInBufferSize;
private int mOutBufferSize;
SharedPreferences mSharedPreferences;
private static boolean mActive = false;
private NotificationManager mNotificationManager;
private MicrophoneReceiver mBroadcastReceiver;
private class MicrophoneReceiver extends BroadcastReceiver {
// Turn the mic off when things get loud
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null && action.equals(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
SharedPreferences prefs = context.getSharedPreferences(APP_TAG, Context.MODE_PRIVATE);
SharedPreferences.Editor e = prefs.edit();
e.putBoolean("active", false);
e.commit();
}
}
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
Log.d(APP_TAG, "Creating mic service");
// notification service
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBroadcastReceiver = new MicrophoneReceiver();
// create input and output streams
mInBufferSize = AudioRecord.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, mFormat);
mOutBufferSize = AudioTrack.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, mFormat);
mAudioInput = new AudioRecord(MediaRecorder.AudioSource.MIC, mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, mFormat, mInBufferSize);
mAudioOutput = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, mFormat, mOutBufferSize, AudioTrack.MODE_STREAM);
// listen for preference changes
mSharedPreferences = getSharedPreferences(APP_TAG, MODE_PRIVATE);
mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
mActive = mSharedPreferences.getBoolean("active", false);
if (mActive)
record();
}
#Override
public void onDestroy() {
Log.d(APP_TAG, "Stopping mic service");
// close the service
SharedPreferences.Editor e = mSharedPreferences.edit();
e.putBoolean("active", false);
e.commit();
// disable the listener
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
mAudioInput.release();
mAudioOutput.release();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.d(APP_TAG, "Service sent intent");
// if this is a stop request, cancel the recording
if (intent != null && intent.getAction() != null) {
if (intent.getAction().equals("net.bitplane.android.microphone.STOP")) {
Log.d(APP_TAG, "Cancelling recording via notification click");
SharedPreferences.Editor e = mSharedPreferences.edit();
e.putBoolean("active", false);
e.commit();
}
}
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// intercept the preference change.
if (!key.equals("active"))
return;
boolean bActive = sharedPreferences.getBoolean("active", false);
Log.d(APP_TAG, "Mic state changing (from " + mActive + " to " + bActive + ")");
if (bActive != mActive) {
mActive = bActive;
if (mActive)
record();
if (!mActive)
mNotificationManager.cancel(0);
}
}
public void record() {
Thread t = new Thread() {
public void run() {
Context context = getApplicationContext();
CharSequence titleText = getString(R.string.mic_active);
CharSequence statusText = getString(R.string.cancel_mic);
long when = System.currentTimeMillis();
Intent cancelIntent = new Intent();
cancelIntent.setAction("net.bitplane.android.microphone.STOP");
cancelIntent.setData(Uri.parse("null://null"));
cancelIntent.setFlags(cancelIntent.getFlags() | Notification.FLAG_AUTO_CANCEL);
PendingIntent pendingCancelIntent = PendingIntent.getService(context, 0, cancelIntent, 0);
Notification notification = new Notification(R.drawable.status, titleText, when);
notification.setLatestEventInfo(context, titleText, statusText, pendingCancelIntent);
mNotificationManager.notify(0, notification);
// allow the
registerReceiver(mBroadcastReceiver, new IntentFilter(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY));
Log.d(APP_TAG, "Entered record loop");
recordLoop();
Log.d(APP_TAG, "Record loop finished");
}
private void recordLoop() {
if ( mAudioOutput.getState() != AudioTrack.STATE_INITIALIZED || mAudioInput.getState() != AudioTrack.STATE_INITIALIZED) {
Log.d(APP_TAG, "Can't start. Race condition?");
}
else {
try {
try { mAudioOutput.play(); } catch (Exception e) { Log.e(APP_TAG, "Failed to start playback"); return; }
try { mAudioInput.startRecording(); } catch (Exception e) { Log.e(APP_TAG, "Failed to start recording"); mAudioOutput.stop(); return; }
try {
ByteBuffer bytes = ByteBuffer.allocateDirect(mInBufferSize);
int o = 0;
byte b[] = new byte[mInBufferSize];
while(mActive) {
o = mAudioInput.read(bytes, mInBufferSize);
bytes.get(b);
bytes.rewind();
mAudioOutput.write(b, 0, o);
}
Log.d(APP_TAG, "Finished recording");
}
catch (Exception e) {
Log.d(APP_TAG, "Error while recording, aborting.");
}
try { mAudioOutput.stop(); } catch (Exception e) { Log.e(APP_TAG, "Can't stop playback"); mAudioInput.stop(); return; }
try { mAudioInput.stop(); } catch (Exception e) { Log.e(APP_TAG, "Can't stop recording"); return; }
}
catch (Exception e) {
Log.d(APP_TAG, "Error somewhere in record loop.");
}
}
// cancel notification and receiver
mNotificationManager.cancel(0);
try {
unregisterReceiver(mBroadcastReceiver);
} catch (IllegalArgumentException e) { Log.e(APP_TAG, "Receiver wasn't registered: " + e.toString()); }
}
};
t.start();
}
}