Create service to detect any action from the user - java

I'm trying to create a service where I want to detect something about user, let's say when user lays the device on a table, the thing is that I have that action detected but I have it on a MainActivty and I want it to put on Service.
The thing is that on my MainActivity() I had my registerAction() and on my onResume() were called and in onPause() I call the unregisterListener() from my sensor, as well I have a HandlerThread where I start it on my onCreate() how do I change it to Service? Would be a problem? I see that there aren't the same methods...
I've created my Service and I've got :
public class MyService extends Service {
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
Log.d("CREATE","ONCREATE");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("DESTROY","ONDESTROY");
}
}
Also my MainActivity I have put implements SensorEventListener.
A skeleton of my class is :
public class MainActivity extends Activity implements SensorEventListener {
private HandlerThread mSensorThread;
private SensorManager mSensorManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorThread = new HandlerThread("sensor_thread");
mSensorThread.start();
}
private void registerSensorListener() {
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST, new Handler(mSensorThread.getLooper()));
}
#Override
public void onSensorChanged(SensorEvent event) {
//DO stuff
if (isLayed()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("LAY","LAYLAY");
}
});
mSensorManager.unregisterListener(this);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private boolean isLayed() {
return stuff;
}
#Override
protected void onResume() {
super.onResume();
registerSensorListener();
}
#Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
}
EDIT
I'm using szamani20 code, but I'm having problems with runOnUiThread because I can not call from my Service also, I'm having this issue
java.lang.RuntimeException: Unable to start service com.example.developer.qwe.MyService#d8c613b with null: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getAction()' on a null object reference

First of all you need to decide whether you want the user to be aware of your running service or not. Take a review on Background Execution Limits in android Oreo:
To improve the user experience, Android 8.0 (API level 26) imposes limitations on what apps can do while running in the background.
So considering your case where it seems there are lots of work to do in many situations, it would be a better approach to use a foreground service. As android document says about foreground services:
A foreground service is a service that the user is actively aware of and is not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
Since you mentioned that you have the action detected I won't enter that part of your code. So you need to create a subclass of Service as you did and use the startService method to get it's onCreate called. One thing you need to notice is that the onCreate method of service is called once you call startService on that service for the first time, no matter how many times you call startService again the onCreate method won't get called and only the onStartCommand get called. We use that fact alongside that you could provide a string action within your intent to properly register and unregister your listener.
In MainActivity.java:
String action = "start"; // Or to unregister listener "stop"!
final Intent intent = new Intent(this, MyService.class);
intent.setAction(action);
startService(intent);
and then in MyService.java:
#Override
public void onCreate() {
super.onCreate();
// Do initialization or whatever here (executed once per service lifecycle)
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals("start")) {
// Register your listener or whatever
showForegroundNotification();
}
if (intent.getAction().equals("stop")) {
// Unregister your listener or whatever
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
private void showForegroundNotification() {
Intent myServiceNotificationIntent = new Intent(this, MainActivity.class);
myServiceNotificationIntent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent
.getActivity(this, MY_SERVICE_REQUEST_CODE,
myServiceNotificationIntent, MY_SERVICE_FLAG);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle(MY_SERVICE_NOTIFICATION_CONTENT_TITLE)
.setTicker(MY_SERVICE_NOTIFICATION_TICKER)
.setContentText(MY_SERVICE_NOTIFICATION_CONTENT_TEXT)
.setSmallIcon(R.drawable.ic_whatever)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(MY_SERVICE_NOTIFICATION_ID, notification);
}
Finally don't forget to unregister your listener in onDestroy in case of android kill your service (which is very rare):
#Override
public void onDestroy() {
super.onDestroy();
// Unregister your listener
}

You can register SensorManager inside service in OnStartCommand.Also try using startForeground as android os will kill your service when app is killed

Related

Making the music play in the background after the app has been closed

My question here is about playing the music after the user closes the app (the app tab is not showing on the list of currently open apps).
I have created a class to handle the service that targets the media player to play the ring-tone music:
public class RingService extends Service {
private MediaPlayer player;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
player = MediaPlayer.create(this,
Settings.System.DEFAULT_RINGTONE_URI);
player.setLooping(true);
player.start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
player.stop();
}
}
in my MainActivity I use a button to start playing music. And it works fine till I close the app.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
}
public void onRing(View view) {
Intent intent = new Intent(this, RingService.class);
startService(intent);
}
public void onStop(View view) {
Intent intent = new Intent(this, RingService.class);
stopService(intent);
}
}
Is there anyone who could tell me, what I'm doing wrong? I would be extremely grateful.
And just not to start another topic for this app, I will ask it here - is it possible to relaunch the activity in 10 seconds after it has been closed by the user?
I think you missed the onCreate to implement
My app is doing good with this code:
public class BackgroundMusicService extends Service {
MediaPlayer musicPlayer;
public IBinder onBind(Intent arg) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
musicPlayer = MediaPlayer.create(this, R.raw.your_music_file);
musicPlayer.setLooping(true); // Set looping
musicPlayer.setVolume(100,100);
}
public int onStartCommand(Intent intent, int flags, int startId) {
musicPlayer.start();
return 1;
}
#Override
public void onDestroy() {
musicPlayer.stop();
musicPlayer.release();
}
and in you manifest:
<service android:enabled="true" android:name=".BackgroundSoundService" />
So after closing your app you want to play some ringtone using RingService but when user close the app the onStop() method is called in your MainActivity with this code Intent intent = new Intent(this, RingService.class);
stopService(intent);. How service can play your music if you stopping your service at same time.
Try to delete this code from onStop() (not sure it will solve the problem but it worth trying).
Additionally I am recommending using Logcat for testing your code.
https://developer.android.com/studio/command-line/logcat
You need to put your service in foreground with startForeground(id, notification), check the official docs for reference.
When you have a service in foreground, the chances of your process being killed are reduced drastically because your process won't be considered as in background. The downside is that you must show a permanent notification, which in your case might even be desirable because you might need controls for the player.

background music doesn't stop if app is onStop Android

I have created a service to play music through my activities. I Have 2 activities and a splashScreen. I start the service from the first acitivity and by clicking some items I go to the second activity. The music is played properly but when I put my app in background or I lock my phone or I'm in another app I still hear the sound. I cannot put stopService in onStop because if I go to my second activity, the music will stop.
Here's my service music class :
public class BackgroundMusicService extends Service {
MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.game_music);
player.setLooping(true);
player.setVolume(10, 10);
player.start();
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return 1;
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
#Override
public void onLowMemory() {
}
}
and :
public class Activity1 extends AppCompatActivity implements View.OnClickListener{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_1);
Intent backgSound = new Intent(Activity1.this, BackgroundMusicService.class);
startService(backgSound);
Button b1 = (Button) findViewById(R.id.b1);
b1.setOnClickListener(this);
}
#Override
public void onDestroy() {
super.onDestroy();
Intent backgSound = new Intent(Activity1.this, BackgroundMusicService.class);
stopService(backgSound);
}
#Override
public void onClick(View v) {
startActivity(new Intent(this,Activity2.class);
}
And the second activity is just a view (for test).
How could I keep the sound over these 2 activites and stop it when my app is in background or my phone is locked.
I've tried to stopService in onStop then startService in onResume, but it does not work between activities.
Judging by what you want you need a more fine grained control over the starting and stopping of your MediaPlayer object. An easy solution would be to add intent-filters and actions like so:
public class BackgroundMusicService extends Service {
public static final String ACTION_START_MUSIC = "package_name.action_start_music";
public static final String ACTION_STOP_MUSIC = "package_name.action_stop";
private MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.game_music);
player.setLooping(true);
player.setVolume(10, 10);
}
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent.getAction() != null){
switch (intent.getAction()){
case ACTION_START_MUSIC :
if(!player.isPlaying()){
player.start();
}
break;
case ACTION_STOP_MUSIC :
if(player.isPlaying()) {
player.stop();
}
break;
default: break;
}
}
return START_STICKY;
}
#Override
public void onDestroy() {
player.release();
}
#Override
public void onLowMemory() {
}
}
Update your manifest :
<service android:name=".BackgroundMusicService"
android:exported="false">
<intent-filter>
<action android:name="package_name.action_start_music" />
<action android:name="package_name.action_stop" />
</intent-filter>
</service>
To use:
startService(new Intent(BackgroundMusicService.ACTION_START_MUSIC));
startService(new Intent(BackgroundMusicService.ACTION_STOP_MUSIC));
It is well known that onDestroy() is not always calling, moving your onDestroy actions to the onPause() method will work:
Note: do not count on this method being called as a place for saving data! For example, if an activity is editing data in a content provider, those edits should be committed in either onPause() or onSaveInstanceState(Bundle), not here. This method is usually implemented to free resources like threads that are associated with an activity, so that a destroyed activity does not leave such things around while the rest of its application is still running. There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
More info here.
You need to read more about the basics of Android.
Regards.

Using CountDownTimer in a RecyclerView and a Service

I need to implement different CountDownTimers in a recyclerView. I've put a BroadcastService class which contains my CountDownTimer. It broadcasts to my MainActivity, where I'm updating the UI with a timer. However, I need to do this for several different timers with variable initial times. When any of these timers hits zero, some unique code needs to be triggered.
Here's an example of how my BroadcastService class:
package com.example.cdt;
import android.app.Service;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.util.Log;
public class BroadcastService extends Service {
private final static String TAG = "BroadcastService";
public static final String COUNTDOWN_BR = "your_package_name.countdown_br";
Intent bi = new Intent(COUNTDOWN_BR);
CountDownTimer cdt = null;
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Starting timer...");
cdt = new CountDownTimer(30000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.i(TAG, "Countdown seconds remaining: " + millisUntilFinished / 1000);
bi.putExtra("countdown", millisUntilFinished);
sendBroadcast(bi);
}
#Override
public void onFinish() {
Log.i(TAG, "Timer finished");
}
};
cdt.start();
}
#Override
public void onDestroy() {
cdt.cancel();
Log.i(TAG, "Timer cancelled");
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
My MainActivity then receives the BroadCast Service's timer as such:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(new Intent(this, BroadcastService.class));
Log.i(TAG, "Started service");
}
private BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateGUI(intent); // or whatever method used to update your GUI fields
}
};
#Override
public void onResume() {
super.onResume();
registerReceiver(br, new IntentFilter(BroadcastService.COUNTDOWN_BR));
Log.i(TAG, "Registered broacast receiver");
}
#Override
public void onPause() {
super.onPause();
unregisterReceiver(br);
Log.i(TAG, "Unregistered broacast receiver");
}
#Override
public void onStop() {
try {
unregisterReceiver(br);
} catch (Exception e) {
// Receiver was probably already stopped in onPause()
}
super.onStop();
}
#Override
public void onDestroy() {
stopService(new Intent(this, BroadcastService.class));
Log.i(TAG, "Stopped service");
super.onDestroy();
}
private void updateGUI(Intent intent) {
if (intent.getExtras() != null) {
long millisUntilFinished = intent.getLongExtra("countdown", 0);
Log.i(TAG, "Countdown seconds remaining: " + millisUntilFinished / 1000);
}
}
Note that this code comes from How to run CountDownTimer in a Service in Android? where I learned how to run CountDownTimer as a service.
I have a completely functional RecyclerView. I just don't know how the logic would work using these classes to have several different timers.
For example, if one timer hits 0:00:00, how will Android know what code block to run on the onFinish()?
Please note that using broadcast receiver for this type of task may be a very heavy solution and you may consider using some sort of callback interfaces.
Having that said, using your current solution, you can use extra's (bi.putExtra(...)) to add resources to the intents you send. Then inside your receiver you can retrieve those values (get*Extra(...); i.e. int identifiers of the timers - just as you do with bi.putExtra("countdown", millisUntilFinished)).
Edit:
Based on comments, I'm not entirely sure of what you would like to do, but maybe this will help:
The onCreate method of the service will get called only once and should be used for general service initialization - every subsequent call to startService will only cause the onStartCommand invocation, which should be used to create your timers.
When you start your service using startService(new Intent(this, BroadcastService.class)), you can also put extras inside the intent. Then the onStartCommand(Intent intent, int flags, int startId) gets called, with intent having your extras, unique to each startService call.
Inside the onStartCommand you can retrieve extras like identifier of the timer and it's requested runtime, create a CountDownTimer and store them somewhere, e.g. a HashMap, where String key would be the identifier.
Inside the onTick method of each timer, you would also put an antoher value - the timer identifier, which you can retrieve on the application side, so you know which timer posted the broadcast.
This way you can manage the timers on both the app and service side, using your identifier. You may want to handle a situations like starting the service with the same identifier, while the timer is still running (cancellation etc.).

How to pause game when user turn off screen

I have problem with one of my games. This is time based puzzle game and I have problem with it. When user press on/off button on Android device game doesn`t stop, but timer goes on and on till game over. When user turn on screen again, he can see game over screen. But I want to make scenario when user press on/off button game will pause.
Any suggestions? I`m pretty new in programming so please explain me very basic method to do this.
Thanks all!
Edit. Timer code
private void initializeProgressBar() {
//initialize progressbar
progress = ApplicationConstants.GAME_TIME;
mProgress = (ProgressBarDetermininate) findViewById(R.id.progressDeterminate);
mProgress.setMax(progress);
mProgress.setProgress(progress );
timer = new Timer();
progressBarUpdateTask = new ProgressBarUpdateTask();
timer.schedule(progressBarUpdateTask, 20, 20);
}
class ProgressBarUpdateTask extends TimerTask {
#Override
public void run() {
runOnUiThread(new Runnable(){
#Override
public void run() {
progress-=1;
if(progress==0)
{
TimeOver();
}
mProgress.setProgress(progress);
}
});
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onPause() {
super.onPause();
this.timer.cancel();
}
Pause your game in onStop() or onPause() (depending on your need) in the Activity context your game is running in.
I am assuming you are using android's activity...
#Override
protected void onResume() {
super.onResume();
// Resume the timer or show a button for the user to press when ready
// !!! Also check if timer exits because onResume is called before onCreate!!!
}
#Override
protected void onPause() {
super.onPause();
// Pause the timer
}
When you press screen off or Power button onPause method of the application will be called and when you again press Power button, applications onResume method will be called, you should pause timer in onPause and resume it in onResume.
One way to do this is to detect the user presence, here's an example
At beginning of your game start the LockService
startService(new Intent(getApplicationContext(), LockService.class));
LockService.java
public class LockService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
final BroadcastReceiver mReceiver = new ScreenReceiver();
registerReceiver(mReceiver, filter);
return super.onStartCommand(intent, flags, startId);
}
public class LocalBinder extends Binder {
LockService getService() {
return LockService.this;
}
}
}
Then finally the BroadcastReceiver where you can stop your game.
ScreenReceiver.java
public class ScreenReceiver extends BroadcastReceiver {
public static boolean wasScreenOn = true;
#Override
public void onReceive(final Context context, final Intent intent) {
Log.e("LOB","onReceive");
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// do whatever you need to do here
wasScreenOn = false;
/* PAUSE THE GAME HERE*/
Log.e("LOB","wasScreenOn"+wasScreenOn);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// and do whatever you need to do here
wasScreenOn = true;
}else if(intent.getAction().equals(Intent.ACTION_USER_PRESENT)){
}
}
}
As correctly mentioned in other answers, you can also use the onPause() method
#Override
protected void onPause() {
super.onPause();
// stop the game
}

Android: Correct way to use a Service in a multi-Activity application

I know, that there are tons of questions regarding leaking Android services and how to "maintain" a service in an app, but it's a bit like seeing two doctors with one problem: you get three different opinions...
In other words: I am completely unsure, how to deal with my service in a proper way. Hence this question.
I have the following: Several Activities and a BoundService, which is started on app startup and should be kept alive until the app closes.
Android.delevoper proposes to use a bound service to ease communication between Service and Activity. So I bind my service in each activitie's onStart() and unbind it in every onStop() to keep it from leaking.
In addition, I read in several threads, that one should use startService() to keep the service from being destroyed when no Activity is binding to it anymore. So I use an explicit startService() in my first Activity and a stopService() on every exit point of my app.
So basically I have this as my Service:
public class NetworkService extends Service {
private final IBinder binder = new ServiceBinder();
#Override
public void onCreate() {
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class ServiceBinder extends Binder {
public NetworkService getService() {
return NetworkService.this;
}
}
}
And then a starting Activity like this:
public class ActFirstActivity extends Activity {
private NetworkService networkService;
private boolean networkServiceIsBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_1);
}
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(this, NetworkService.class);
startService(serviceIntent);
bindService(serviceIntent, networkServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(networkServiceConnection);
}
private ServiceConnection networkServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
ServiceBinder binder = (ServiceBinder) service;
networkService = binder.getService();
networkServiceIsBound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
networkServiceIsBound = false;
}
};
}
Every other Activity would have the same bind/unbind functionality and the same ServiceConnection. As far as I can see, this keeps my app from leaking a service connection. But I have the impression that I introduced a LOT of overhead here. How to do this
a) secure (no leaking),
b) efficient (minimal overhead), and
c) easy to maintain (code-wise) ?
Thanks to everyone who takes the time to answer :)

Categories