0

I am having trouble to stop the alarm from ringing even after I have pressed the stopAlarm button. I cannot figure out how to get access to the same Ringtone instance and call stop().

This is my start alarm switch in MainActivity java class.

public void switchClicked(View view) {
    if (((Switch) view).isChecked()) {
        Log.d("MainActivity", "Alarm On");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, alarmTimePicker.getHour());
        calendar.set(Calendar.MINUTE, alarmTimePicker.getMinute());
        Intent myIntent = new Intent(MainActivity.this, AlarmReceiver.class);
        pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, 0);
        alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), pendingIntent);
        setAlarmText("ON");


    } else {
        alarmManager.cancel(pendingIntent);
        setAlarmText("OFF");
        Log.d("MainActivity", "Alarm Off");
    }
}

public void setAlarmText(String alarmText) {
    alarmTextView.setText(alarmText);
}

Here is my StopAlarm button in MainActivity java class.

public void stopAlarm(View view) {
        setAlarmText("Alarm stopped");
        Intent myIntent = new Intent(MainActivity.this, AlarmReceiver.class);
        pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        alarmManager.cancel(pendingIntent);
    }

This is the AlarmReciver java class.

public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {

    MainActivity inst = MainActivity.instance();
    inst.setAlarmText("Alarm! Wake up! Wake up!");
    Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
    if (alarmUri == null) {
        alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    }
    Ringtone ringtone = RingtoneManager.getRingtone(context, alarmUri);
    ringtone.play();
}
}
Zoe
  • 27,060
  • 21
  • 118
  • 148
  • 2
    Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the [CC BY-SA 3.0 license](https://creativecommons.org/licenses/by-sa/3.0/)). By SE policy, any vandalism will be reverted. – Zoe Apr 03 '19 at 20:08
  • 2
    Please don't make more work for other people by vandalizing your posts. By posting on Stack Overflow, you've granted a non-revocable right, under the [CC BY-SA 3.0 license](https://creativecommons.org/licenses/by-sa/3.0) for SO to distribute that content. By SO policy, any vandalism will be reverted. If you want to know more about deleting a post, please read more at [How does deleting work?](https://meta.stackexchange.com/q/5221) – iBug Apr 03 '19 at 20:09

1 Answers1

0

steps:

add RingtonePlayingService to your project (don't forget to declare it in manifest)

public class RingtonePlayingService extends Service{

    private Ringtone ringtone = null;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId){
        Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
        if (alarmUri == null) 
            alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        this.ringtone = RingtoneManager.getRingtone(this, alarmUri);
        ringtone.play();

        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent){
        return null;
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        shutUpRingtone();
    }

    private void shutUpRingtone(){
        if(ringtone != null)
            ringtone.stop();
        ringtone = null;
    }
}

remove code you have inside onReceive and put this:

@Override
public void onReceive(final Context context, Intent intent) {
    Intent startIntent = new Intent(context, RingtonePlayingService.class);
    context.startService(startIntent);
}

now RingtonePlayingService is handling ringtone playing and keeps reference to it. Your stopAlarm method is just cancelling potentially pending alarm, so if it didn't fired yet until stop button click it won't never fire. But when it started ringing already then you should stop ringtone instance held by started service. Add on the end of your method:

public void stopAlarm(View view) {
    ...
    Intent stopIntent = new Intent(context, RingtonePlayingService.class);
    stopService(stopIntent);
}

Now stopAlarm do both: cancels pending alarm (if any) and stops service (if is alive), which is potentially playing ringtone

also remove MainActivity.instance() pattern, it is bad as hell... When you set future alarm (e.g. now+1h) and system (or user) remove app from memory before it gets fired then you will get NullPointerException, as .instance() is null, and you are immediatelly trying to access inst.setAlarmText method. Intead of you may use Local Broadcasting or service binding to update your UI - send broadcasts/messages to your Activity from working RingtonePlayingService with start/stop/current status

snachmsm
  • 17,866
  • 3
  • 32
  • 74