As soon as my application starts I get a crash and I can't figure out why. I have a Service that is used as a timer to count the number of seconds since the timer started, this then gets passed back to my MainActivity.
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "BroadcastTest";
private Intent intent;
private Chronometer timer;
private Button start;
private Button stop;
private EditText hourlyRate;
private TextView money;
private long timeWhenStopped = 0;
private double moneyEarned;
private double secondsRate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer = (Chronometer) findViewById(R.id.chronometer);
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
hourlyRate = (EditText) findViewById(R.id.hourlyRate);
money = (TextView) findViewById(R.id.money);
intent = new Intent(this, Timer.class);
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startService(new Intent(MainActivity.this, Timer.class));
timer.setBase(SystemClock.elapsedRealtime() + timeWhenStopped);
timer.start();
}
});
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timeWhenStopped = timer.getBase() - SystemClock.elapsedRealtime();
timer.stop();
}
});
timer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener()
{
#Override
public void onChronometerTick(Chronometer chronometer){
long timeElapsed = SystemClock.elapsedRealtime() - timer.getBase();
int seconds = (int) timeElapsed / 1000;
double hrlyRate = Double.parseDouble(hourlyRate.getText().toString());
secondsRate = (hrlyRate / 60) / 60;
moneyEarned = secondsRate * seconds;
double displayMoney = Math.round(moneyEarned * 100.0)/100.0;
Log.d("hours",Long.toString(seconds));
money.setText("$"+Double.toString(displayMoney));
}
});
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateUI(intent);
}
};
#Override
public void onResume() {
super.onResume();
startService(intent);
registerReceiver(broadcastReceiver, new IntentFilter(Timer.BROADCAST_ACTION));
}
#Override
public void onPause() {
super.onPause();
unregisterReceiver(broadcastReceiver);
stopService(intent);
}
private void updateUI(Intent intent) {
String counter = intent.getStringExtra("counter");
Log.d("SERVICE_SECONDS", counter);
}
}
Timer.java:
public class Timer extends Service {
private static final String TAG = "BroadcastService";
public static final String BROADCAST_ACTION = "b.calvin.com.dirtymoney";
private final Handler handler = new Handler();
Intent intent;
int counter = 0;
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
countSeconds();
handler.postDelayed(this, 1000); // 1 second
}
};
#Override
public void onCreate()
{
super.onCreate();
intent = new Intent(BROADCAST_ACTION);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 0); // 1 second
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void countSeconds() {
Log.d(TAG, Integer.toString(++counter));
sendBroadcast(intent);
}
}
Error:
FATAL EXCEPTION: main
Process: b.calvin.com.dirtymoney, PID: 3348
java.lang.RuntimeException: Error receiving broadcast Intent { act=b.calvin.com.dirtymoney flg=0x10 } in b.calvin.com.dirtymoney.MainActivity$4#bf337ff
at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:891)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: println needs a message
at android.util.Log.println_native(Native Method)
at android.util.Log.d(Log.java:139)
at b.calvin.com.dirtymoney.MainActivity.updateUI(MainActivity.java:113)
at b.calvin.com.dirtymoney.MainActivity.access$600(MainActivity.java:20)
at b.calvin.com.dirtymoney.MainActivity$4.onReceive(MainActivity.java:93)
at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:881)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Seems like you forgot to put your counter variable into the Intent, so you're passing null to Log.d() as the message, resulting in a NullPointerException.
Something like this should work:
private void countSeconds() {
Log.d(TAG, Integer.toString(++counter));
Intent counterIntent = new Intent(BROADCAST_ACTION);
counterIntent.putExtra("counter", counter);
sendBroadcast(counterIntent);
}
Also, counter is an int, so you should call getIntExtra() instead of getStringExtra():
private void updateUI(Intent intent) {
int counter = intent.getIntExtra("counter");
Log.d("SERVICE_SECONDS", Integer.toString(counter));
}
Related
I am making an android app that can send notifications even if the app is closed. So I added a NotificationService to call when the app is destroyed. But I get an error: System services not available to Activities before onCreate() when I force the app to stop.
This is the MainActivity.java:
public class MainActivity extends AppCompatActivity {
private Button button1;
private Button button2;
private Button button3;
private Button button4;
private static final String CHANNEL_ID = "telus";
private static final String CHANNEL_NAME = "telus app";
private static final String CHANNEL_DESC = "telus app notification";
private int count = 1;
Timer timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1=(Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openActivity_web();
}
});
button2=(Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openActivationPage();
}
});
button3=(Button) findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openMysql();
}
});
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(CHANNEL_DESC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
displayNotification();
}
});
class sendnotif extends TimerTask {
public void run() {
displayNotification();
}
}
Timer timer = new Timer();
timer.schedule(new sendnotif(), 0, 5000);
}
public void displayNotification(){
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_alarm)
.setContentTitle("Titre de la notification")
.setContentText("C'est le text de la notification")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
;
NotificationManagerCompat mNotifMgr = NotificationManagerCompat.from(this);
mNotifMgr.notify( 1, mBuilder.build());
}
public void openActivity_web(){
Intent intent = new Intent(this, WebPage.class);
startActivity(intent);
}
public void openActivationPage(){
Intent intent = new Intent(this, ActivationPage.class);
startActivity(intent);
}
public void openMysql(){
Intent intent = new Intent(this, mysql_display.class);
startActivity(intent);
}
protected void onDestroy(){
super.onDestroy();
startService(new Intent(this, NotificationService.class));
}
}
and this is the NotificationService.java:
public class NotificationService extends Service {
Timer timer;
TimerTask timerTask;
String TAG = "Timers";
int Your_X_SECS = 5;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onCreate() {
Log.e(TAG, "onCreate");
}
#Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
stoptimertask();
super.onDestroy();
}
//we are going to use a handler to be able to run in our TimerTask
final Handler handler = new Handler();
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, after the first 5000ms the TimerTask will run every 10000ms
timer.schedule(timerTask, 5000, Your_X_SECS * 1000); //
//timer.schedule(timerTask, 5000,1000); //
}
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
//use a handler to run a toast that shows the current timestamp
handler.post(new Runnable() {
public void run() {
MainActivity notif = new MainActivity();
notif.displayNotification();
}
});
}
};
}
}
This is the error log when I try to manually stop the app:
2019 - 07 - 16 20: 10: 23.079 23334 - 23334 / com.example.testapp E / AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testapp, PID: 23334
java.lang.IllegalStateException: System services not available to Activities before onCreate()
at android.app.Activity.getSystemService(Activity.java: 5581)
at androidx.core.app.NotificationManagerCompat. < init > (NotificationManagerCompat.java: 158)
at androidx.core.app.NotificationManagerCompat.from(NotificationManagerCompat.java: 153)
at com.example.testapp.MainActivity.displayNotification(MainActivity.java: 103)
at com.example.testapp.NotificationService$1$1.run(NotificationService.java: 89)
at android.os.Handler.handleCallback(Handler.java: 751)
at android.os.Handler.dispatchMessage(Handler.java: 95)
at android.os.Looper.loop(Looper.java: 154)
at android.app.ActivityThread.main(ActivityThread.java: 6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java: 866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java: 756)
Thank you for your help.
Purpose of program: I'm trying to make an app that will count how many times the user checked their phone by issuing a broadcast for Intent.ACTION_SCREEN_ON. it then increments a counter and updates the activity with the new counter.
The problem: This all works just fine but as soon as I swipe away the application from the apps tray, the counter goes back to zero.
obviously what is supposed to happen is the counter would continue.
I tried saving the counter value in the service onDestroy and then calling it again onCreate but onDestroy is never called.
Note that in the onCreate() for the activity it sends a broadcast to the service asking for the most recent value of counter and then updates it in the view. I couldn't find a better way to keep them in sync.
CounterService.java
public class CounterService extends Service {
public static boolean RERUN = true;
private int counter = 0;
private SharedPreferences SP;
private BroadcastReceiver mScreenStateBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
counter++;
System.out.println("************************************* \n \n " + counter);
}
sendCounterBroadcast();
}
};
public void sendCounterBroadcast() {
Intent i = new Intent();
i.setAction("com.inc.count");
i.putExtra("counterValue", counter);
sendBroadcast(i);
}
#Override
public void onCreate() {
super.onCreate();
System.out.println("********************** CounterService.onCreate()");
// get counter value from SP -- this is useful for when the service gets recreated
SP = getSharedPreferences("Counter Service Data", MODE_PRIVATE);
counter = SP.getInt("counter", 0);
// wait for screen to be turned on or for the activity to ask you for the counter number
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction("send.counter.to.phonecounteractivity");
registerReceiver(mScreenStateBroadcastReceiver, intentFilter);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
System.out.println("***************************************** CounterService.OnDestroy()");
unregisterReceiver(mScreenStateBroadcastReceiver);
// Save counter value for when we restart service
SP = getSharedPreferences("Counter Service Data", MODE_PRIVATE);
SharedPreferences.Editor SPE = SP.edit();
if (RERUN) {
SPE.putInt("counter", counter);
System.out.println("******************************** RESTARTING SERVICE ");
startService(new Intent(getApplicationContext(), CounterService.class));
} else
SPE.putInt("counter", 0);
SPE.apply();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
PhoneCheckerCounter.Java
public class PhoneCheckerCounter extends AppCompatActivity {
private BroadcastReceiver changeCount;
private IntentFilter filter;
private int counter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phone_checker_counter);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
switcherOnClick();
assignValuesOnCreate();
System.out.println("**************************** onCreate()");
changeCounterText();
}
public void switcherOnClick() {
final Switch sCounter = findViewById(R.id.switchCounter);
sCounter.setOnClickListener(new View.OnClickListener() {
Intent intent = new Intent(getApplicationContext(), CounterService.class);
#Override
public void onClick(View v) {
if (sCounter.isChecked()) {
startService(intent);
CounterService.RERUN = true;
v.getContext().registerReceiver(changeCount, filter);
Toast.makeText(getApplicationContext(), "Counting has begun", Toast.LENGTH_SHORT).show();
} else {
TextView n = findViewById(R.id.counter);
n.setText("0");
CounterService.RERUN = false;
v.getContext().unregisterReceiver(changeCount);
stopService(intent);
Toast.makeText(getApplicationContext(), "The application stopped counting", Toast.LENGTH_SHORT).show();
}
}
});
}
public void changeCounterText() {
changeCount = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
TextView n = findViewById(R.id.counter);
counter = intent.getIntExtra("counterValue", 0);
System.out.println("************************ RECEIVED!!!! value of: " + counter);
n.setText("" + counter);
}
};
filter = new IntentFilter();
filter.addAction("com.inc.count");
this.registerReceiver(changeCount, filter);
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(changeCount);
assignValuesOnDestroy();
System.out.println("**************************** onDestroy()");
}
public void assignValuesOnCreate() {
Switch s = findViewById(R.id.switchCounter);
if (getSwitchValueFromSP() == 1) s.setChecked(true);
else s.setChecked(false);
Intent f = new Intent();
f.setAction("send.counter.to.phonecounteractivity");
sendBroadcast(f);
}
public void assignValuesOnDestroy() {
SharedPreferences SP = getSharedPreferences("data", MODE_PRIVATE);
SharedPreferences.Editor edit = SP.edit();
Switch s = findViewById(R.id.switchCounter);
if (s.isChecked()) edit.putInt("switch", 1);
else edit.putInt("switch", 0);
edit.apply();
}
public int getSwitchValueFromSP() {
SharedPreferences SP = getSharedPreferences("data", MODE_PRIVATE);
int isOn = SP.getInt("switch", 0);
return isOn;
}
}
Sample of the activity
On the first activity I want to set the number with a number picker and I have this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer_settings);
NumberPicker np = (NumberPicker)findViewById(R.id.numberPicker2);
np.setMinValue(1);
np.setMaxValue(999);
np.setWrapSelectorWheel(false);
np.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
Intent timer2 = new Intent(timer_settings.this, timer_2.class);
Bundle timer2extras = new Bundle();
timer2extras.putString("timer2string", newVal + "");
timer2.putExtras(timer2extras);
}
});
final Button testbutton = (Button) findViewById(R.id.testbutton);
testbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent testintent = new Intent(timer_settings.this, timer_2.class);
startActivity(testintent);
}
});
}
And on the second activity I want to use the number as the start time for the countdown, and I have this:
public class timer_2 extends AppCompatActivity {
ImageButton imageButton3;
private TextView timer_2_up;
private TextView timer_2_down;
private CountDownTimer timer_2_up_countdowntimer;
private CountDownTimer timer_2_down_countdowntimer;
private boolean timer_2_up_running;
private boolean timer_2_down_running;
private Bundle timer2extras = getIntent().getExtras();
private String timer2string = timer2extras.getString("timer2string");
private long starttime = Integer.parseInt(timer2string);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer_2);
timer_2_up = findViewById(R.id.timer_2_up);
timer_2_down = findViewById(R.id.timer_2_down);
imageButton3 = (ImageButton)findViewById(R.id.imageButton3);
imageButton3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imageButton3.animate().rotation(imageButton3.getRotation()+180).start();
imageButton3.setEnabled(false);
Timer buttonTimer = new Timer();
buttonTimer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
imageButton3.setEnabled(true);
}
});
}
}, 300);
if (timer_2_up_running){
pausetimer_2_up();
starttimer_2_down();
}else {
starttimer_2_up();
if (timer_2_down_running) {
pausetimer_2_down();
}
}
}
});
}
private void starttimer_2_up() {
timer_2_up_countdowntimer = new CountDownTimer(starttime, 1000) {
#Override
public void onTick(long millisUntilFinished) {
starttime = millisUntilFinished;
update_up_text();
}
#Override
public void onFinish() {
timer_2_up_running=false;
}
}.start();
timer_2_up_running = true;
}
private void starttimer_2_down() {
timer_2_down_countdowntimer = new CountDownTimer(starttime, 1000) {
#Override
public void onTick(long millisUntilFinished) {
starttime = millisUntilFinished;
update_down_text();
}
#Override
public void onFinish() {
timer_2_down_running=false;
}
}.start();
timer_2_down_running=true;
}
private void pausetimer_2_up () {
timer_2_up_countdowntimer.cancel();
timer_2_up_running=false;
}
private void pausetimer_2_down() {
timer_2_down_countdowntimer.cancel();
timer_2_down_running=false;
}
private void update_up_text() {
int minutes_up = (int) (starttime / 1000) / 60;
int seconds_up = (int) (starttime / 1000) % 60;
String time_2_up_left_formatted = String.format(Locale.getDefault(),"%02d:%02d", minutes_up, seconds_up);
timer_2_up.setText(time_2_up_left_formatted);
}
private void update_down_text() {
int minutes_down = (int) (starttime / 1000) / 60;
int seconds_down = (int) (starttime / 1000) % 60;
String time_2_down_left_formatted = String.format(Locale.getDefault(),"%02d:%02d", minutes_down, seconds_down);
timer_2_down.setText(time_2_down_left_formatted);
}
But it does not work showing null errors that I cant find a solution to.
I don't know if this is because of the long/int difference between the NumberPicker and the countdown timer or I forgot to add some line.
Please help me.
Because you are put bundle extra to a Intent, then start another Intent. You don't have to watch for NumberPicker's value changed event. Just getValue() whenever you want to start timer activity.
In your first activity, it should be
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer_settings);
NumberPicker np = (NumberPicker)findViewById(R.id.numberPicker2);
np.setMinValue(1);
np.setMaxValue(999);
np.setWrapSelectorWheel(false);
final Button testbutton = (Button) findViewById(R.id.testbutton);
testbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent testintent = new Intent(timer_settings.this, timer_2.class);
Bundle timer2extras = new Bundle();
timer2extras.putString("timer2string", np.getValue() + "");
timer2.putExtras(timer2extras);
startActivity(testintent);
}
});
}
In the timer activity, put the code that getIntent().getExtras() inside onCreate() when everything is ready.
private Bundle timer2extras;
private String timer2string;
private long starttime;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer_2);
// Find views
timer2extras = getIntent().getExtras();
timer2string = timer2extras.getString("timer2string");
starttime = Integer.parseInt(timer2string);
// ...
}
Here's code for a timer that plays a sound once it reaches 0 (timer works fine). The problem is the sound persists even through onPause() in MainActivity.java called.
I implemented onDestroy() in SimpleIntentService.java to stop the sound, but apparently it's never called even with finish() in the calling Activity. How am I supposed to make the sound stop when the app is paused?
Here's my MainActivity.java
public class MainActivity extends Activity {
private BroadcastReceiver broadcastReceiver;
NumberPicker picker;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
picker = (NumberPicker) findViewById(minutePicker);
Log.i("TurnToTech", "Project Name - SimpleBackgroundService");
picker.setMinValue(0);
picker.setMaxValue(20);
broadcastReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
String text = intent.getStringExtra(SimpleIntentService.PARAM_OUT_MSG);
Toast.makeText(getApplicationContext(),
text, Toast.LENGTH_SHORT).show();
}
};
}
Intent msgIntent;
public void startTimer(View view) {
setContentView(R.layout.activity_main);
msgIntent = new Intent(this, SimpleIntentService.class);
msgIntent.putExtra(SimpleIntentService.PARAM_IN_MSG, "Alarm: ");
msgIntent.putExtra("time", picker.getValue());
startService(msgIntent);
}
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(SimpleIntentService.ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(broadcastReceiver,filter);
}
public void onPause() {
finish();
unregisterReceiver(broadcastReceiver);
super.onPause();
}
}
And the SimpleIntentService.java
public class SimpleIntentService extends IntentService {
public static final String PARAM_IN_MSG = "in_msg";
public static final String PARAM_OUT_MSG = "out_msg";
int time;
public static final String ACTION_RESP = "org.turntotech.intent.action.MESSAGE_PROCESSED";
public SimpleIntentService() {
super("SimpleIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
System.out.println("SimpleIntentService Called");
String msg = intent.getStringExtra(PARAM_IN_MSG);
int time = intent.getIntExtra("time", 0);
// Timer implementation
if (time == 0 ){
playSound();
}
while(time > 0){
SystemClock.sleep(5000); // 5 seconds
time -= 5;
String resultTxt = msg + time + " seconds remaining";
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(ACTION_RESP);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(PARAM_OUT_MSG, resultTxt);
broadcastIntent.putExtra("time", time);
sendBroadcast(broadcastIntent);
if (time == 0) {
playSound();
}
}
}
Uri alert;
public void playSound(){
alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), alert);
r.play();
}
public void onDestroy() {
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), alert);
r.stop();
super.onDestroy();
}
}
In your IntentService you're not really stopping the same alarm in your onDestroy function. Because each time you're getting a new instance of it.
So I would like to suggest to keep a public static variable of Ringtone so that it can be accessed from everywhere. Declare them in your MainActivity.
public static Ringtone r;
public static Uri alert;
Initialize them in the onCreate function of your MainActivity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ... Other statements
// Initialize ringtone here
initializeRingtone();
}
private void initializeRingtone() {
alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
r = RingtoneManager.getRingtone(getApplicationContext(), alert);
}
Now the onPause() function of your MainActivity should look like this
public void onPause() {
unregisterReceiver(broadcastReceiver);
r.stop();
super.onPause();
}
And if you want to play the sound after you resume the application from background and then the timer runs out, you might consider doing something like this in the onResume function of your MainActivity
public void onResume() {
super.onResume();
registerReceiver(broadcastReceiver);
initializeRingtone(); // Initialize it again.
}
And the playSound() function in the IntentService might look like this.
public void playSound(){
// Initialize the alert and ringtone again.
MainActivity.alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
MainActivity.r = RingtoneManager.getRingtone(getApplicationContext(), alert);
MainActivity.r.play();
}
public void onDestroy() {
MainActivity.r.stop();
super.onDestroy();
}
Hope that helps!
I have a Service that I start inside an Activity, but everytime the Service tries send the Broadcast back to the Activity I get this error:
07-22 17:14:21.890: E/AndroidRuntime(22445):
java.lang.RuntimeException: Error receiving broadcast Intent {
act=com.test.test.countDown flg=0x10 (has extras) } in
com.test.test.CookingTimer$1#1de35657 07-22 17:14:21.890:
E/AndroidRuntime(22445): at
android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:880)
07-22 17:14:21.890: E/AndroidRuntime(22445): at
android.os.Handler.handleCallback(Handler.java:739)
This is how I call the service:
timerIntent = new Intent(CookingTimer.this, TimerService.class);
startService(timerIntent);
registerReceiver(broadcastReceiver, new IntentFilter(TimerService.BROADCAST_ACTION));
This is my broadcast receiver:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateUI(intent);
}
};
And finally this is my Service class:
public class TimerService extends Service {
private Intent intent;
public static final String BROADCAST_ACTION = "com.test.test.countDown";
private Handler handler = new Handler();
private long initial_time;
long timeInMilliseconds = 0L;
#Override
public void onCreate() {
super.onCreate();
initial_time = SystemClock.uptimeMillis();
intent = new Intent(BROADCAST_ACTION);
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 1000); // 1 second
}
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
DisplayLoggingInfo();
handler.postDelayed(this, 1000); // 1 seconds
}
};
private void DisplayLoggingInfo() {
timeInMilliseconds = SystemClock.uptimeMillis() - initial_time;
int timer = (int) timeInMilliseconds / 1000;
intent.putExtra("time", timer);
sendBroadcast(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(sendUpdatesToUI);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
Can someone explain me why I get that error and how can I possibly fix it? Maybe i'm missing some declaration inside my Manifest?