I have developed an Android app that has a background service that runs endlessly and saves on a local SqLite DB the results of bluetooth scan and GPS positions. Only on Huawei devices this service seems to be paused or stopped for some minutes (I noticed that after inserting some log into the code): in theese minutes any log is written.
. I tried without success to change some settings of device (battery optimization).
Do you have some advice to solve the problem?
Below you can find a snipped of the service.
public class MyService extends Service {
public MyService() {
super();
}
#Override
public void onCreate() {
super.onCreate();
...
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(1031, getNotification());
}
final Intent serviceIntent = new Intent(getApplicationContext(), MyService.class);
ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyServiceBinder binder = (MyServiceBinder) service;
started = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
started = false;
}
};
bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
}
#RequiresApi(Build.VERSION_CODES.O)
private Notification getNotification() {
NotificationChannel channel = new NotificationChannel("channel_01", "My Channel", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
Notification.Builder builder = new Notification.Builder(getApplicationContext(), "channel_01");
builder.setContentTitle(getString(R.string.app_name))
.setAutoCancel(true)
.setColor(getResources().getColor(R.color.colorAccent))
.setContentText(getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_stat_onesignal_default);
return builder.build();
}
public class MyServiceBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
private void stopForegroundService()
{
// Stop foreground service and remove the notification.
stopForeground(true);
// Stop the foreground service.
stopSelf();
}
#Override
public void onDestroy() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForegroundService();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Restart after 5 secs
Handler h = new Handler(Looper.getMainLooper());
h.postDelayed(new Runnable() {
#Override
public void run() {
GenericUtility.launchService(MyService.class, getApplication());
}
}, 5000);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
...
...
initScanLoop();
initLocationManager();
return Service.START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return new MyServiceBinder();
}
#Override
public boolean onUnbind(Intent intent) {
boolean res = super.onUnbind(intent);
return res;
}
/*Init bluetooth handler*/
private void initScanLoop() {
final Handler h = new Handler(Looper.getMainLooper());
h.post(new Runnable() {
#Override
public void run() {
scanLeDevice();
hBeacon.postDelayed(this, SCAN_DURATION + 10000);
}
});
}
private void scanLeDevice() {
if(mLEScanner != null && !scanning.get() && !stopScan) {
scanning.set(true);
mLEScanner.startScan(null, settings, mScanCallback);
Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if(scanning.get()) {
stopScanLeDevice();
}
}
}, SCAN_DURATION);
}
}
private void stopScanLeDevice() {
scanning.set(false);
if(mLEScanner != null) {
mLEScanner.stopScan(mScanCallback);
}
}
/*Finish bluetooth handler*/
/*Init GPS handler*/
private void initLocationManager() {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
createLocationChangedCallback();
locationListener = new BeaconScanLocationListener(locationChangedCallback);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}
private void createLocationChangedCallback() {
locationChangedCallback = new LocationChangedCallback() {
#Override
public void callback(final Location location) {
try {
//GPS callcback
} catch(Exception e) {
}
}
#Override
public void enabledDisabled(boolean enabled) {
}
};
}
/*Finish GPS handler*/
}
UPDATE
I improved app functionality replacing bluetooth scanning with monitoring beacon in region function of Android Beacon Library.
You have to configure in settings for app can run in background for Huawei
you can check out this link here
This problem can you fix it by put your device in App launch normally the app manage by Huawei but you must make it manage manually after that Huawei can't put the service in sleep mode.
Related
When the app is in the foreground can get calls well, but when is in the background and a call is active and the run app: it can not get that's call.
When the app is in the foreground, get call works well (get push-notification and connecting is being automatically, also you can push "Incoming Call" notification to connecting call).
I fixed it in swift but I cant fix it for android.
ios fixed with this funcs:
func applicationDidEnterBackground(_ application: UIApplication) {
if !isCallScreen {
UIApplication.shared.unregisterForRemoteNotifications()
}
}
func funBack() {
let state = UIApplication.shared.applicationState
if state == .background {
UIApplication.shared.unregisterForRemoteNotifications()
}
if isCallScreen {
isCallScreen = false
DispatchQueue.main.async {
self.window?.rootViewController?.navigationController?.popViewController(animated: true)
}
}
}
Messaging service
public class MessagingService extends FirebaseMessagingService {
private int notificationNumber = 1;
private LocalBroadcastManager broadcaster;
private int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
private DatabaseReference mDatabase;
private SinchClient sinchClient;
private SharedPref pref;
public static boolean firsttimeFun = true;
#Override
public void onCreate() {
broadcaster = LocalBroadcastManager.getInstance(this);
pref = new SharedPref(this);
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//
if (SinchHelpers.isSinchPushPayload(remoteMessage.getData())) {
NotificationResult result = sinchClient.relayRemotePushNotificationPayload(
remoteMessage.getData());
}
if (!AppLifecycleListener.isActivityVisible()) {
AppLifecycleListener.activityResumed();
}
// it's NOT Sinch message - process yourself
class FcmListenerService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage){
Map data = remoteMessage.getData();
if (SinchHelpers.isSinchPushPayload(data)) {
new ServiceConnection() {
private Map payload;
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (sinchClient != null) {
NotificationResult result = sinchClient.relayRemotePushNotificationPayload(payload);
// handle result, e.g. show a notification or similar
}
}
#Override
public void onServiceDisconnected(ComponentName name) {}
public void relayMessageData(Map<String, String> data) {
payload = data;
getApplicationContext().bindService(new Intent(getApplicationContext(), CallActivity.class), this, BIND_AUTO_CREATE);
}
}.relayMessageData(data);
}
}
}
// String bundleString = remoteMessage.getNotification().getBody();
//
// String bundleString = remoteMessage.getData().get("message");
Map bundle = remoteMessage.getData();
String callId = bundle.get("callID").toString();
String groupname = bundle.get("group").toString();
try {
bundle.get("hangup").toString();
Intent intent = new Intent("MyData");
intent.putExtra("callID", callId);
broadcaster.sendBroadcast(intent);
return;
} catch (Exception e) {
e.printStackTrace();
}
//IF app is in background use return for ignore notifications
if (!AppLifecycleListener.isActivityVisible()) {
sendNotification(callId, groupname);
//return;
}
Log.i("MessageFrom", "From: " + callId);
if (firsttimeFun) {
firsttimeFun = false;
startActivity(new Intent(this, CallActivity.class).putExtra("callerId", callId).putExtra("name", groupname).putExtra("isOutGoing", false).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
if (AppLifecycleListener.isActivityVisible())
sendNotification(callId, groupname);
//return;
// sendNotification(bundle.get("message").toString());
}
#Override
public void onNewToken(String s) {
/* HashMap map=new HashMap();
map.put("fcmToken",s);
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("Users").child(pref.getUserId()).updateChildren(map);*/
}
#RequiresApi(api = Build.VERSION_CODES.N)
private void sendNotification(String callId, String groupname) {
PendingIntent contentIntent;
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
mNotificationManager = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, CallActivity.class).putExtra("callerId", callId).putExtra("name", groupname).putExtra("isOutGoing", false), 0);
int notifyID = 1;
String CHANNEL_ID = "CallAppChanel";// The id of the channel.
CharSequence name = getString(R.string.channel_name);// The user-visible name of the channel.
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel mChannel = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mChannel = new NotificationChannel(CHANNEL_ID, "Channel 1", importance);
}
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.icon)
.setContentTitle("Incoming Call")
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(contentIntent)
.setColor(getResources().getColor(R.color.purple))
.setAutoCancel(true)
.setOnlyAlertOnce(true)
.setChannelId(CHANNEL_ID)
.setPriority(importance)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.build();
int notificationId = new Random().nextInt(100) + 1;
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mNotificationManager.createNotificationChannel(mChannel);
mNotificationManager.notify(notificationId, notification);
} else {
mNotificationManager.notify(notificationId, notification);
}
}
}
AppLifecycleListener
public class AppLifecycleListener extends Application implements Application.ActivityLifecycleCallbacks, LifecycleObserver {
private Context context;
Activity activityRunning;
private static Application instance;
private static boolean activityVisible;
private boolean inForeground;
public void setContext(Context context) {
this.context = context;
}
public Context getContext() {
return this.context;
}
public static Context getContextApp() {
return instance.getApplicationContext();
}
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
/*getVersionRequest(this);*/
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
activityRunning = activity;
}
#Override
public void onActivityStarted(#NonNull Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
// Utility.logCatMsg("Activity Resume : " + activity.getLocalClassName());
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener((Activity) context, new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
String newToken = instanceIdResult.getToken();
Log.e("newToken",newToken);
}
});
}
#Override
public void onActivityPaused(Activity activity) {
// Utility.logCatMsg("Activity Pause : " + activity.getLocalClassName());
}
#Override
public void onActivityStopped(Activity activity) {
// Utility.logCatMsg("Activity Stop : " + activity.getLocalClassName());
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
// Utility.logCatMsg("Activity Destroyed : " + activity.getLocalClassName());
}
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
}
I'm developing an app that is going to require 2 services:
HostService - A foreground service that manages music playback using the Spotify app-remote SDK. It is necessary to run this service in the foreground as I need to detect playState changes and manage playback constantly. However, this service ONLY needs to run if a condition is met, we'll call that condition isHost. This service will be bound to a SessionActivity that displays the music playing and manages playback.
ApiService - A service that continuously polls a separate web API for updates. This must also run in the foreground if the isHost condition is met as the updates may influence the music playback. However, if the user is not a host, this can be a background process that only needs to poll while the user is actively using a particular Activity. I'm not sure if this service should also be bound to the activity or not.
My question is, how should I go about setting up the ApiService differently depending on this condition? I've started to implement these services below but I'm not entirely sure what I have done makes sense. Additionally, am I doing the right thing by using an event handler the way I am? I don't fully understand Intents and I'm not sure if I should be using broadcast messages and such instead.
SessionActivity.java
class SessionActivity extends AppCompatActivity implements HostEventHandler, ApiEventHandler {
private boolean isHost;
private String data;
private String UID;
private ApiService api;
private HostService host;
private boolean hostBound;
private boolean apiBound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_session);
isHost = getIntent().getBooleanExtra("isHost", false);
data = getIntent().getStringExtra("data");
UID = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
}
private ServiceConnection hostConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(Constants.TAG, "Bound to HostService");
HostService.LocalBinder binder = (HostService.LocalBinder) service;
host = (HostService) binder.getService()
host.registerEventHandler(this);
hostBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.e(Constants.TAG, "onServiceDisconnected");
hostService = null;
}
};
private ServiceConnection apiConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(Constants.TAG, "Bound to ApiService");
HostService.LocalBinder binder = (ApiService.LocalBinder) service;
api = (ApiService) binder.getService()
api.registerEventHandler(this);
apiBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.e(Constants.TAG, "onServiceDisconnected");
apiService = null;
}
};
protected void onStart() {
super.onStart();
Log.d(Constants.TAG, "Binding to services...");
Intent intent = new Intent(this, ApiService.class);
intent.putExtra("data", data);
intent.putExtra("user", Constants.UID);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
if (isHost) {
Intent intent = new Intent(this, HostService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
{
}
#Override
protected void onStop() {
super.onStop();
unbindService(apiConnection);
apiBound = false;
if (isHost) {
unbindService(hostService);
hostBound = false;
}
}
// HostEventHandler and ApiEventHandler methods...
}
HostService.java
public class HostService extends Service {
private final IBinder binder = new LocalBinder();
private HostEventHandler handler;
public class LocalBinder extends Binder {
HostService getService() {
return HostService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.d(Constants.TAG, "onBind");
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
Intent notificationIntent = new Intent(this, SessionActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("Foreground service is running...")
.setSmallIcon(R.drawable.logo)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
connect(); // This connects to spotify
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
disconnect();
}
public registerEventHandler(HostEventHandler handler) {
this.handler = handler;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
ApiService.java
public class ApiService extends Service {
private final IBinder binder = new LocalBinder();
private ApiEventHandler handler;
private final Timer timer;
private boolean connected = false;
private String data;
private String UID;
private ApiState state;
public class LocalBinder extends Binder {
HostService getService() {
return ApiService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.d(Constants.TAG, "onBind");
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!connected) {
data = intent.getStringExtra("data");
UID = intent.getStringExtra("user");
connect();
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
disconnect();
}
public void registerEventHandler(ApiEventHandler handler) {
this.handler = handler;
}
public ApiState getState() {
return state;
}
private void connect() {
if (connected) return;
class Updater extends TimerTask {
#Override
public void run() {
state = fetchUpdatedState(); // This will implement the web request
handler.onStateUpdate(state);
Log.d(Constants.TAG, "Polling API...");
timer.schedule(new Updater(), Constants.REFRESH_DELAY);
}
}
timer.schedule(new Updater(), Constants.REFRESH_DELAY);
connected = true;
}
}
I need to create a service that allow my application to work also when I close it, I’ve tried with STICKY_SERVICE but it doesn’t work... if anyone can decribe me how I can do this please answer this question.
It works with android 7.1 and it doesn’t with other versions
Here is my code...
public class SensorService extends Service {
public int counter=0;
public SensorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
stoptimertask();
Intent broadcastIntent = new Intent("RestartSensor");
sendBroadcast(broadcastIntent);
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i("in timer", "in timer ++++ "+ (counter++));
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
This is my service Restarter...
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops");
context.startService(new Intent(context, SensorService.class));
}
}
And the mainClass
public class MainActivity extends AppCompatActivity {
Intent mServiceIntent;
private SensorService mSensorService;
Context ctx;
public Context getCtx() {
return ctx;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
setContentView(R.layout.activity_main);
mSensorService = new SensorService(getCtx());
mServiceIntent = new Intent(getCtx(), mSensorService.getClass());
if (!isMyServiceRunning(mSensorService.getClass())) {
startService(mServiceIntent);
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("isMyServiceRunning?", true+"");
return true;
}
}
Log.i ("isMyServiceRunning?", false+"");
return false;
}
#Override
protected void onDestroy() {
stopService(mServiceIntent);
Log.i("MAINACT", "onDestroy!");
super.onDestroy();
}
}
This restartthe service correctly but after 3/4 seconds it die.
I've added this to my manifest
<service
android:name=".SensorService"
android:enabled="true" >
</service>
<receiver
android:name=".SensorRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped">
<intent-filter>
<action android:name="RestartSensor"/>
</intent-filter>
</receiver>
Add the following code to your sensor service and edit as per your need. You need to bind the sticky service to a notification to keep the service alive and start in the foreground.
#Override
public void onCreate() {
super.onCreate();
Intent notifIntent = new Intent(this, SensorService.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, notifIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.smslogo_100x100)
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
.setContentTitle(getResources().getString(R.string.app_name))
.setContentText("Running")
.setContentIntent(pi)
.build();
startForeground(101010, notification);
}
This will rectify the issue you are facing and the service will run forever.
I am trying to develop an application where the user can know the status of his/her Bluetooth Le device once he/she launches the app and the connection status is updated in text view in Home Fragment.I have tried to implement it by sending Broadcast from Ble service and catching it in onResume of Home fragment and subsequently updating it in status text view. The status does gets updated but if I change the fragment and come back to the home fragment the text view to show the status gets blank although the Bluetooth Le device is connected.How can I resolve this problem so that the status shows connected all through out if the device is connected and disconnected if it is disconnected?
Any Kind of guidance will be highly appreciated.
here are the code segments I have used to implement the above
In Bleservice.java
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
In HomeFragment.java
public class HomeFragment extends Fragment
{
private BroadcastReceiver mReceiver;
#Override
protected void onResume() {
super.onResume();
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Bleservice.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true;
updateConnectionState(R.string.connected);
} else if (Bleservice.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
updateConnectionState(R.string.disconnected);
}
}
};
getActivity().registerReceiver(mReceiver,makeGattUpdateIntentFilter());
}
private void updateConnectionState(final int resourceId) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
tv_connected_disconnected.setText(resourceId);
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
getActivity().unregisterReceiver(mReceiver);
}
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Bleservice.ACTION_GATT_CONNECTED);
intentFilter.addAction(Bleservice.ACTION_GATT_DISCONNECTED);
return intentFilter;
}
Connect to your BleService by bindService. Check out example with Activity and Service.
private BleService mBluetoothLeService;
private boolean isConnected;
#Override
protected void onStart() {
super.onStart();
Intent bindIntent = new Intent(this, BleService.class);
startService(bindIntent);
bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
registerServiceReceiver(); //register here your mGattCallback that get actions from BleService
}
#Override
protected void onStop() {
super.onStop();
unbindService(mServiceConnection);
mBluetoothLeService = null;
LocalBroadcastManager.getInstance(this).unregisterReceiver(mGattUpdateReceiver);
}
In method bindService you need to pass a ServiceConnection that manage Service Lifecycle.
// Code to manage Service lifecycle.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
mBluetoothLeService = ((BleService.LocalBinder) service).getService();
isConnected = (mBluetoothLeService.getConnectionState() != BleService.STATE_CONNECTED)
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBluetoothLeService = null;
}
};
In Service you need to declare Binder.
private final IBinder mBinder = new LocalBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalBinder extends Binder {
public BleService getService() {
return BleService.this;
}
}
public int getConnectionState() {
return mConnectionState;
}
So now, after binding to BleService, you can get a connection state.
I have a service that runs in the background and every 15 minutes, it will show a notification or dialog. But how can I get it to start and stop OnClick of a FAB?
The button shows a Snack Bar OnClick, currently. I want to add If/Else code to start and stop the service. How can I do this?
Here is the service:
public class SafeService extends Service {
private Handler mHandler = new Handler();
private Timer mTimer = null;
public static final int NOTIFICATION_ID = 1;
public static final long NOTIFY_INTERVAL = 900 * 1000; // 15 Minutes
/*^TODO - TEST NOTIFY_INTERVAL FOR ACCURACY^*/
/*^Those are for the timer and handler so the code
can recognise it^ The last one gives how long the timer runs */
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
// Cancels the timer if it already existed.
if (mTimer != null) {
mTimer.cancel();
} else {
// recreate new
mTimer = new Timer();
}
// schedule task
mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 0, NOTIFY_INTERVAL);
}
class TimeDisplayTimerTask extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
// Lollipop or Above
if (Build.VERSION.SDK_INT >= 21) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(SafeService.this);
new NotificationCompat.Builder(SafeService.this);
builder.setSmallIcon(R.drawable.smallplaceholder);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContentTitle("Safeguard");
builder.setContentText("Be careful, Trainer! Remember to look up and stay aware of your surroundings!!");
builder.setStyle(new NotificationCompat.BigTextStyle().bigText("Be careful, Trainer! Remember to look up and stay aware of your surroundings!!"));
builder.setPriority(Notification.PRIORITY_HIGH);
builder.setVibrate(new long[] { 1000, 1000, 1000, 1000, 1000 });
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, builder.build());
//Below Lollipop
} else {
new MaterialDialog.Builder(SafeService.this)
.title(R.string.warning_title)
.content(R.string.warning)
.positiveText(R.string.button_ok)
.show();
}
}
});
}
};
}
Here is the button I want to start and stop the service:
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Safeguard is now on!", Snackbar.LENGTH_LONG).show();
//Start service after showing SnackBar
}
});
You can use a Singleton instance which will bind/unbind from your service, this way, the service will not be unbound if your activity is destroyed by Android :
ServiceSingleton.java :
public class ServiceSingleton {
private String TAG = ServiceSingleton.class.getSimpleName();
private static ServiceSingleton mInstance;
private Context mContext;
private boolean mBound = false;
private SafeService mService;
private ServiceConnection mServiceConnection = null;
public static ServiceSingleton getInstance(Context context) {
if (mInstance == null)
mInstance = new ServiceSingleton(context);
return mInstance;
}
private ServiceSingleton(Context context) {
this.mContext = context.getApplicationContext();
}
public boolean startNotification() {
if (!mBound) {
mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
Log.i(TAG, "onServiceConnected");
mService = ((SafeService.LocalBinder) service).getService();
mService.startNotification();
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
Intent intent = new Intent(this, SafeService.class);
mBound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
if (!mBound) {
Log.e(TAG, "Error cant bind to service !");
}
} else {
if (mService != null) {
mService.startNotification();
}
}
return mBound;
}
public void stopNotification() {
if (mBound && mService != null) {
mService.stopNotification();
}
}
public boolean isNotificationStarted() {
if (mBound && mService != null) {
return mService.isNotificationStarted();
}
return false;
}
public void close() {
try {
if (mBound) {
if (mService!=null){
mService.stopNotification();
}
mContext.unbindService(mServiceConnection);
mBound = false;
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
public boolean isBound() {
return mBound;
}
}
In your onCreate()
mSingleton = ServiceSingleton.getInstance();
For your click listener :
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mSingleton.isNotificationStarted()){
mSingleton.startNotification();
}
else {
mSingleton.stopNotification();
}
}
});
stopNotification() wont unbind service if you want to reuse it, if you want to shut it down call close()