I try do do some background calculation tasks in an Android application.
My Main class :
public class MainActivity extends AppCompatActivity {
private CalculationReceiver calculationReceiver = new CalculationReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
calculationReceiver.doAddition(mContext, 2, 2);
}
});
}
}
My service :
public class CalculationService extends IntentService {
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
CalculationReceiver.completeWakefulIntent(intent);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
}
My receiver :
public class CalculationReceiver extends WakefulBroadcastReceiver {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, CalculationService.class);
int receiverNumber1 = intent.getIntExtra(NUMBER_1,0);
int receiverNumber2 = intent.getIntExtra(NUMBER_2,0);
service.putExtra(NUMBER_1,receiverNumber1);
service.putExtra(NUMBER_2,receiverNumber2);
startWakefulService(context, service);
}
public void doAddition (Context context, int number1, int number2){
Intent intent = new Intent(context, CalculationReceiver.class);
intent.putExtra(NUMBER_1,number1);
intent.putExtra(NUMBER_2,number2);
context.sendBroadcast(intent);
}
}
My Manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testservices">
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".ReductionService"
android:enabled="true" />
<receiver android:name=".ReductionReceiver"/>
<service android:name=".CalculationService"
android:enabled="true" />
<receiver android:name=".CalculationReceiver"/>
</application>
</manifest>
The calculations of the application are more complex than these additions, and can take several minutes (in average 15 minutes) to be done.
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
The idea is that the user starts his calculation and then waits for the application to give the result. In the meantime, he can launch other apps or lock his phone, the calculation must not stop.
This approach seems to work.
What bothers me here is the call to service in the receiver:
context.sendBroadcast (intent);
Is there a more "clean" way to start the service?
What strikes me is that it does not seem very "clean", especially the passage of several times the same parameter (number1 and number2)
Thanks
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
That is not how the documentation shows using WakefulBroadcastReceiver. Plus, WakefulBroadcastReceiver was deprecated in version 26.0.0 of the support libraries.
The idea is that the user starts his calculation and then waits for the application to give the result.
My interpretation of this is that the user is requesting, through your activity's UI, to start the calculation. This means that at this point in time, the screen is on and you have an activity in the foreground.
Is there a more "clean" way to start the service?
Call startService().
Step #1: Delete your use of the deprecated WakefulBroadcastReceiver
Step #2: Have your activity call startService() to start the service
Step #3: Have your service acquire a partial WakeLock through the PowerManager system service, in the service's onCreate() method
Step #4: Have your service release that WakeLock in the service's onDestroy() method
Step #5: Modify the service to be a foreground service, calling startForeground() in onCreate() with a suitable Notification to allow the user to control the behavior of the service
Note that:
If you skip Step #5, your service will stop running after ~1 minute on Android 8.0+.
This will still not work on Android 6.0+ if the device enters into Doze mode. That should not happen for ~1 hour, but you need to make sure that your calculations are done by then.
Consider offloading the calculation work to a server, rather than burning up the user's CPU for an extended period of time (through your calculation work plus the wakelock)
It was in this way that I resolved it, following the advice of #CommonsWare
Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent service = new Intent(mContext, CalculationService.class);
service.putExtra(NUMBER_1,2);
service.putExtra(NUMBER_2,2);
startService(service);
}
});
}
}
Calculation Service :
public class CalculationService extends IntentService {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
private static final int FOREGROUND_ID = 42;
PowerManager.WakeLock wakeLock;
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
#Override
public void onCreate() {
super.onCreate();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"CalculationServiceWakelockTag");
wakeLock.acquire();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Calculation App")
.setContentText("Calculation in progress")
.setContentIntent(pendingIntent).build();
startForeground(FOREGROUND_ID, notification);
}
#Override
public void onDestroy() {
super.onDestroy();
wakeLock.release();
}
}
This is working great :)
But can I call
startForegroundService(service)
instead of
startService(service);
or not ? And why ?
Related
I'm trying to make an app that monitors the users phone usage by tracking time of screen lock and unlock. I tried to setup a BroadcastReceiver which works fine when the app is running the background. But won't work when I close the app. Is there a solution for this.
The code I'm using now is as follows :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, ScreenListenerService.class);
startService(intent);
}
}
ScreenListenerService class is as follows..
public class ScreenListenerService extends Service {
private BroadcastReceiver mScreenStateBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// Save something to the server
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// Save something to the server
}
}
};
#Override
public void onCreate() {
super.onCreate();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenStateBroadcastReceiver, intentFilter);
}
#Override
public void onDestroy() {
unregisterReceiver(mScreenStateBroadcastReceiver);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
My AndroidManifest file is as follows :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.abbinvarghese.calculu">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<service android:name=".ScreenListenerService" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
To overcome the imposed limitations of 8.0 you could run a foreground service. Just like a service but a notification is posted to the foreground.
Then the service code would be like this (remember to unregister the receiver onDestory):
BroadcastReceiver screenReceiver;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startRunningInForeground();
detectingDeterminateOfServiceCall(intent.getExtras());
registerBroadcastReceivers();
return START_STICKY;
}
private void startRunningInForeground() {
//if more than or equal to 26
if (Build.VERSION.SDK_INT >= 26) {
//if more than 26
if(Build.VERSION.SDK_INT > 26){
String CHANNEL_ONE_ID = "sensor.example. geyerk1.inspect.screenservice";
String CHANNEL_ONE_NAME = "Screen service";
NotificationChannel notificationChannel = null;
notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_MIN);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (manager != null) {
manager.createNotificationChannel(notificationChannel);
}
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.background_running);
Notification notification = new Notification.Builder(getApplicationContext())
.setChannelId(CHANNEL_ONE_ID)
.setContentTitle("Recording data")
.setContentText("ActivityLog is logging data")
.setSmallIcon(R.drawable.background_running)
.setLargeIcon(icon)
.build();
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
startForeground(101, notification);
}
//if version 26
else{
startForeground(101, updateNotification());
}
}
//if less than version 26
else{
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Activity logger")
.setContentText("data recording on going")
.setSmallIcon(R.drawable.background_running)
.setOngoing(true).build();
startForeground(101, notification);
}
}
private Notification updateNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
return new NotificationCompat.Builder(this)
.setContentTitle("Activity log")
.setTicker("Ticker")
.setContentText("recording of data is on going")
.setSmallIcon(R.drawable.activity_log_icon)
.setContentIntent(pendingIntent)
.setOngoing(true).build();
}
private void detectingDeterminateOfServiceCall(Bundle b) {
if(b != null){
Log.i("screenService", "bundle not null");
if(b.getBoolean("phone restarted")){
storeInternally("Phone restarted");
}
}else{
Log.i("screenService", " bundle equals null");
}
documentServiceStart();
}
private void documentServiceStart() {
Log.i("screenService", "started running");
}
private void registerBroadcastReceivers() {
screenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
switch (Objects.requireNonNull(intent.getAction())){
case Intent.ACTION_SCREEN_ON:
//or do something else
storeInternally("Screen on");
break;
case Intent.ACTION_SCREEN_OFF:
//or do something else
storeInternally("Screen off");
break;
}
}
};
IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_ON);
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenReceiver, screenFilter);
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(screenReceiver);
}
and call it from the main activity:
private void startServiceRunning() {
if(!isMyServiceRunning(Background.class)){
if(Build.VERSION.SDK_INT >25){
startForegroundService(new Intent(this, Background.class));
}else{
startService(new Intent(this, Background.class));
}
}
}
As Background Execution Limit imposes on Android 8.0 (API level 26) so now it's not possible to listen SCREEN_OFF and SCREEN_ON action in background by running the service.
I have found a work around for same with the help of JobScheduler which works fine for listen broadcast in background without running any service.
Please check on this: Screen OFF/ON broadcast listener without service on Android Oreo
Instead of creating a new service for broadcast receiver, you can directly create a broadcast receiver class that will listen to system broadcasts even when the app is not running.
Create a new class which extends BroadcastReceiver.
public class YourReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Do your stuff
}
}
And register it in manifest.
<receiver
android:name=".YourReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_SCREEN_ON" />
<action android:name="android.intent.action. ACTION_SCREEN_OFF" />
<category android:name="android.intent.category.DEFAUL" />
</intent-filter>
</receiver>
Read about Manifest-declared receivers here.
Above solution won't work, here is the reason why. Problem is that your service is getting killed when the app is killed, so your receiver instance is removed from memory. Here is a little trick to re-start the service in background. Add the following code to your service.
#Override
public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
Although this is not the right way to do it. Also in Android 26+ you won't be able to do this and you'd go for foreground service. https://developer.android.com/about/versions/oreo/background
So I have two different apps made, one sends a broadcast and another receives it and displays a toast. However, when I close the receiver app the broadcast is no longer received by the second app even though I defined the receiver in the manifest file.
The broadcast sender in the MainActivity of app1.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button b = (Button)findViewById(R.id.button2);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent();
i.setAction("com.example.ali.rrr");
i.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(i);
Log.e("Broadcast","sent");
}
});
}
App 2 broadcast receiver:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context, "Broadcast has been recieved!", Toast.LENGTH_SHORT).show();
Log.e("SUCCESS", "IN RECIEVER");
//throw new UnsupportedOperationException("Not yet implemented");
}
App 2s Manifest:
<?xml version="1.0" encoding="utf-8"?>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.ali.rrr" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main"
android:theme="#style/AppTheme.NoActionBar" />
<activity
android:name=".Main2Activity"
android:label="#string/title_activity_main2"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
After registering my BroadcastReceiver (BR) statically in the manifest, applying the proper intent filters, using JobIntentService (and registering it in the manifest) to handle the work that was called from my BR, I was still getting inconsistent results.
Once all of what I listed above has been done you should be able to send an ADB command to activate your broadcast and process the work in your service even if the app is closed. This was working for me some of the time, but not all of the time.
This article describes limitation to BRs.
"As of Android 3.1 the Android system excludes all receiver from receiving intents by default if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu" (AKA a user executes Force Stop)
When I start the app by debugging it, then swipe it closed on my device, my ADB command never activates my BR. However, after my debugging session is over, when I open up the app on my device and swipe it closed, I can activate my BR through ADB commands.
This occurs because when you debug an application, then manually swipe it closed on the device, Android considers this a Force Stop hence why my BR cannot be activated until I re-open the app on the device without debugging.
Scoured the internet for hours, and wasn't able to find my solution, so I thought I'd post it here just in case some poor unfortunate soul is encountering the same weird functionality I was.
Happy coding :)
First of all you need to use the Service for this functionality to work.
In the Activity you can start and stop the service by using the below codes.
// to start a service
Intent service = new Intent(context, MyBrodcastRecieverService.class);
context.startService(service);
// to Stop service
Intent service = new Intent(context, MyBrodcastRecieverService.class);
context.stopService(service);
Then you can use the below service
public class MyBrodcastRecieverService extends Service
{
private static BroadcastReceiver br_ScreenOffReceiver;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public void onCreate()
{
registerScreenOffReceiver();
}
#Override
public void onDestroy()
{
unregisterReceiver(br__ScreenOffReceiver);
m_ScreenOffReceiver = null;
}
private void registerScreenOffReceiver()
{
br_ScreenOffReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.d(TAG, "ACTION_SCREEN_OFF");
// do something, e.g. send Intent to main app
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(br_ScreenOffReceiver, filter);
}
}
I faced this issue recently. The BroadcastReceiver was working fine even if the app was removed from the background in the emulator and Samsung phones. But it failed to start my app in Chinese manufactured phones like Realme, Mi etc. While struggling to find a way to fix this I found that in the app details page there is battery optimisation settings where the Auto-launch feature was disabled. After I enabled it the app was working fine and BroadcastReceiver was able to start the app. I was unable ti find a way to enable this setting programmatically but I found this question which helped me direct the user to that setting page.
You can go through below solution;
Activity.java
Intent intent=new Intent(MainActivity.this,BroadcastService.class);
startService(intent);
BroadcastService.java
public class BroadcastService extends Service {
private static MusicIntentReceiver br_ScreenOffReceiver;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public void onCreate()
{
registerScreenOffReceiver();
}
#Override
public void onDestroy()
{
}
private void registerScreenOffReceiver()
{
br_ScreenOffReceiver = new MusicIntentReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", -1);
switch (state) {
case 0:
Log.e("AAAAAAAAAA", "Headset is unplugged");
break;
case 1:
Log.e("AAAAAAAAA", "Headset is plugged");
break;
default:
Log.e("AAAAAAAAAAAA", "I have no idea what the headset state is");
}
}
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
registerReceiver(br_ScreenOffReceiver, filter);
}
}
Menifest
<service android:enabled="true" android:name=".BroadcastService" />
Try this way..
Intent i = new Intent();
i.setAction("com.example.ali.rrr");
i.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
i.setComponent(
new ComponentName("PackageNameApp2","PackageNameApp2.MainActivity"));
sendBroadcast(i);
So I need a service for my app, but I need it to keep running all the time until the user press the end Button in the app. It goes like this : The user press start, my service start (starting a notification btw), then 2-3h or more later the user has to click on the notification which get him back to the app where he can press the "End" button and end the service. I call this a Start-End cycle for simplicity purposes.
My user will use many Start-End cycles a day, but the problem is my notification goes away after some time when the phone his lock.
I've already been able to put the notification and service thanks to Stackoverflow so I hope you have a solution for this too :)!
Here is my main activity :
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkEnabled();
mTimeListenerD.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //Button to set the Start time
setDebutTime();
checkEnabled();
new Thread(new Runnable() {
#Override
public void run() {
Intent start=new Intent(MainActivity.this,TimeService.class);
start.putExtra("StopSig",false);
start.putExtra("Temps_travail",mTempsTravail); //Envoi Long
start.putExtra("mSmallest",mSmallest); //Envoi Long
start.putExtra("file_src",src); //Envoi String
start.putExtra("file_dst",dst); //Envoi String
start.putExtra("Debut",sDebut); //Envoi String
start.putExtra("Fin",sFin); //Envoi String
start.putExtra("Name",sName);//Envoi String
start.putExtra("String_travail",sTempsTravail); //Envoi String
start.putExtra("Smallest",sSmallest);//Envoi String*/
startService(start);
}
}).start();
}
});
mTimeListenerF.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //Button to set the End Time
setFinTime();
checkEnabled();
new Thread(new Runnable() {
#Override
public void run() {
Intent end = new Intent(MainActivity.this,TimeService.class);
end.putExtra("StopSig",true);
end.putExtra("TimeF",mTimeF);
end.putExtra("Temps_travail",mTempsTravail); //Envoi Long
end.putExtra("mSmallest",mSmallest); //Envoi Long
end.putExtra("file_src",src); //Envoi String
end.putExtra("file_dst",dst); //Envoi String
end.putExtra("Debut",sDebut); //Envoi String
end.putExtra("Fin",sFin); //Envoi String
end.putExtra("Name",sName);//Envoi String
end.putExtra("String_travail",sTempsTravail); //Envoi String
end.putExtra("Smallest",sSmallest);
// startService(end);
stopService(end);
}
}).start();
}
});
mMail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendMail();
}
}); //Send data by mail
Here is the service i use, with the method that show the notification:
public class TimeService extends Service {
#Override
public void onCreate() {
Log.i(TAG, "Service onCreate");
mContext=TimeService.this;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service onStartCommand");
Toast.makeText(this,"TimeServiceStarted",Toast.LENGTH_LONG).show();
stopSig=intent.getBooleanExtra("StopSig",false); //Receiving Boolean
setDebutTime();
setNotification();
//Stop service once it finishes its task
//stopSelf();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
TSTempsTravail=arg0.getLongExtra("Temps_travail",TSTempsTravail); //Receiving Long
TSSmallest=arg0.getLongExtra("mSmallest",TSSmallest);//Receiving Long
sName=arg0.getStringExtra("Name");//Receiving String
sDebut= arg0.getStringExtra("Debut");//Receiving String
sFin= arg0.getStringExtra("Fin");//Receiving String
sTempsTravail= arg0.getStringExtra("String_travail");//Receiving String
sSmallest=arg0.getStringExtra("Smallest");//Receiving String
return null;
}
#Override
public void onDestroy() {
stopSig=true;
setFinTime();
// getName();
getHeureSupp(TimeD,TimeF);
Clock();
writeToFile(sDebut,sFin,sName,sTempsTravail);
try {
copyFile(src,dst);
} catch (IOException e) {
e.printStackTrace();
}
//sendMail();
Log.i(TAG, "Service onDestroy");
//Toast.makeText(this,"Service stopped",Toast.LENGTH_LONG).show();
}
public void setNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
/*notificationIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);*/
PendingIntent pendingIntent = PendingIntent.getActivity(this, 15, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.logo);
NotificationCompat.Builder notification = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setContentTitle("myTitle")
.setTicker("myTicker")
.setContentText("myText")
.setSmallIcon(R.drawable.logo_small)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true);
startForeground(15, notification.build());
}
And finally here is the Manifest if that can be useful in anyway :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.benjii.myapp">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<application
android:allowBackup="true"
android:icon="#drawable/logo_small"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".TimeService"
android:icon="#drawable/logo_small"
android:label="#string/service">
</service>
</application>
</manifest>
Application components (services, activities, etc) always run in main thread, no matter what thread they are started from. Consider starting thread in your Service instead, or use an IntentService.
I'm trying to acquire accelerometer data using an Android Wear app on a smartwatch (Samsung Gear Live). To acquire that data I use a service which listens to three components:
a SensorEventListener, where the onSensorChanged method triggers a SensorEventLoggerTask that stores the accelerometer data with timestamps in a file on the smartwatch
a System intent that listens to the battery
an Intent that listens button clicks on an activity. The activity is used to annotate the data, so I have an idea at what timestamp an (person)activity (running, eating, sleeping,...) was started:
This works fine and I can sample the data every (more or less) 5-6 ms. However this only works when the smartwatch is connected through adb or when the activity is active (to press a button). From the moment it is not connected anymore, there are gaps in the data. With gaps I mean that the time between two timestamps of accelerometer values is much larger than the 5-6 ms. Going to seconds... The gaps appear irregular. But when the activity becomes active (to press a button) or I connect the smartwatch to the adb, the values are gathered. It appears that the service is paused/sleeps/shut down, when the smartwatch is not active.
Below I post the code. The project consists of two classes WearableActivity and WearableService. Furthermore, I also show the Manifest. Allthough this is only prototyping code, any help in resolving the data gaps or suggestions to the code, would be greatly appreciated.
WearableActivity.java
public class WearableActivity extends Activity {
//Set strings and widgets
private static final String TAG = "WearableActivity";
private TextView mTextView;
private Button[] buttons;
private String[] strings;
#Override
protected void onCreate(Bundle savedInstanceState) {
//setscreen
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wearable);
//check if service is already running
if(this.check() == false)
this.startService(new Intent(this, WearableService.class));
Resources res = this.getResources();
strings = res.getStringArray(R.array.button_names);
buttons = new Button[strings.length];
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
#Override
public void onLayoutInflated(WatchViewStub stub) {
mTextView = (TextView) stub.findViewById(R.id.text);
mTextView.setText("ActiMon_Store Started");
buttons[0] = (Button)stub.findViewById(R.id.button1);
buttons[1] = (Button)stub.findViewById(R.id.button2);
buttons[2] = (Button)stub.findViewById(R.id.button3);
buttons[3] = (Button)stub.findViewById(R.id.button4);
buttons[0].setText(strings[0]);
buttons[1].setText(strings[1]);
buttons[2].setText(strings[2]);
buttons[3].setText(strings[3]);
for(Button b : buttons)
b.setOnClickListener(btnListener);
}
});
}
//---create an anonymous class to act as a button click listener---
private OnClickListener btnListener = new OnClickListener()
{
public void onClick(View v)
{
Intent buttonIntent = new Intent("button_activity");
Button b = (Button)v;
buttonIntent.putExtra("time", Long.toString(System.currentTimeMillis()));
buttonIntent.putExtra("button", b.getText().toString());
sendBroadcast(buttonIntent);
Toast.makeText(getApplicationContext(),
"Event sent", Toast.LENGTH_LONG).show();
}
};
//Checks is the service WearableService already started
public boolean check(){
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
{
if ("com.example.WearableService"
.equals(service.service.getClassName()))
{
Log.i(TAG,"Service already running!");
return true;
}
}
Log.i(TAG,"Service already running!");
return false;
}
}
WearableService.java
public class WearableService extends Service implements SensorEventListener {
private PrintStream ps;
private PrintStream ps_bat;
private PrintStream ps_button;
private String androidpath;
private float[] gravity = new float[3];
private float[] linear_acceleration = new float[3];
private PowerManager pm;
private PowerManager.WakeLock wl;
private SensorManager mSensorManager;
private Sensor mAcceleroSensor;
//Create the service and register the listeners
#Override
public void onCreate(){
super.onCreate();
this.registerListeners();
}
//Registers the SensorManager, the listener to the battery and the intent
private void registerListeners(){
mSensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
mAcceleroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this,mAcceleroSensor,SensorManager.SENSOR_DELAY_FASTEST);
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(battery_receiver, ifilter);
this.registerReceiver(broadcastReceiver, new IntentFilter("button_activity"));
}
//Start service as at STICKY
#Override
public int onStartCommand(Intent intent, int flags, int startId){
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
this.getFile();
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE,"KeepCPUWorking");
wl.acquire();
return Service.START_STICKY;
}
//When sensor value has changed
public void onSensorChanged(SensorEvent event){
if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
//Perform a background task to store the data
new SensorEventLoggerTask().execute(event);
}
}
//Get handles to files where data has to be stored
public void getFile(){
androidpath = Environment.getExternalStorageDirectory().toString();
try{
ps = new PrintStream(new FileOutputStream(androidpath + "/data_acc.dat"));
ps_bat = new PrintStream(new FileOutputStream(androidpath + "/data_bat.dat"));
ps_button = new PrintStream(new FileOutputStream(androidpath + "/data_button.dat"));
} catch (Exception e){
e.printStackTrace();
}
}
//Create receiver that listens to Intents from the Battery
private BroadcastReceiver battery_receiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent arg1) {
int bLevel = arg1.getIntExtra("level", -1); // gets the battery level
int bScale = arg1.getIntExtra("scale", -1);
ps_bat.println("" + System.currentTimeMillis() + ";" + bLevel + ";" + bScale);
}
};
#Override
public void onDestroy(){
if(wl.isHeld()) wl.release();
}
// For receiving the broadcast event of the buttons.
private BroadcastReceiver broadcastReceiver = new
BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("button") + ";" + intent.getStringExtra("time");
ps_button.println(msg);
}
};
//Create a Task that logs events to a file
private class SensorEventLoggerTask extends AsyncTask<SensorEvent, Void, Void>{
#Override
protected Void doInBackground(SensorEvent... events) {
//Getting the event and values
SensorEvent event = events[0];
String msg = "" + event.values[0] + ";" + event.values[1] + ";" + event.values[2];
//constructing the the line
msg = "" + System.currentTimeMillis() + ";" + msg;
//writing to file
ps.println(msg);
return null;
}
}
}
Manifest.xml
`<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wearabledemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-feature android:name="android.hardware.type.watch" />
<uses-sdk
android:minSdkVersion="20"
android:targetSdkVersion="20" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#android:style/Theme.DeviceDefault" >
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<activity
android:name=".WearableActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:enabled="true"
android:name=".WearableService"
android:label="#string/app_name"
android:exported="true"
android:permission="android.permission.WAKE_LOCK" >
</service>
</application>
</manifest>`
I am just in process by learning Android a bit, and i have stumbled on this problem.
I want to do a "custom incoming call screen". My current Solution is a class(IncomingCallInterceptor) that extends from BroadcastReceiver. In IncomingCallInterceptor class i override the onReceive and starting my activity(MainActivity) with layout when the phone is ringing.
In that activity(MainActivity) i have three buttons:
Accept Call, Hang Up, Decline Call
Those buttons should do what they say, Answer the phone, hang up the phone or decline the call.
I have in someway got the Accept Call to work, but not Hang Up and Decline.
Heres my code below:
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.uppgift.six.one.incoming61.sixone" >
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver android:name="IncomingCallInterceptor">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
</activity>
</application>
</manifest>
IncomingCallInterceptor that extends from BroadcastReceiver:
public class IncomingCallInterceptor extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Context appContext = context.getApplicationContext();
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String msg = "Phone state changed to " + state;
if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
String incomingNumber = intent.getStringExtra
(TelephonyManager.EXTRA_INCOMING_NUMBER);
msg += ". Incoming number is this " + incomingNumber;
//START MY ACTIVITY!
Intent i = new Intent(appContext, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(i);
}
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
}
Here is my Activity(The Layout is nothing to post, just now is basically three buttons)
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnAnswer = (Button)findViewById(R.id.btnAnswer);
Button btnDecline= (Button)findViewById(R.id.btnDecline);
Button btnHangUp= (Button)findViewById(R.id.btnHangUp);
btnAnswer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_HEADSETHOOK));
sendOrderedBroadcast(i, null);
}
});
btnDecline.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Decline Call (I need help here)
}
});
btnHangUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Hang Up Call (I need help here)
}
});
}
In the MainActivity class it is marked by comments where i need some help.
I have also seen something about a "Telephonyservice interface"(thingy) solution, but i don't understand how that worked when i was testing it.
Reject Call:
try {
TelephonyManager tm = (TelephonyManager)
getSystemService(Context.TELEPHONY_SERVICE);
try {
Class c = Class.forName(tm.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
Object telephonyService = m.invoke(tm); // Get the internal ITelephony object
c = Class.forName(telephonyService.getClass().getName()); // Get its class
m = c.getDeclaredMethod("endCall"); // Get the "endCall()" method
m.setAccessible(true); // Make it accessible
m.invoke(telephonyService); // invoke endCall()
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
}
}