Reputation: 241
In my alarmclock project, I have added pendingIntent
. I want that when the alarm fires, it should be able to pop up a notification message (so that user knows the alarm is triggering) even if the application is closed/minimized/or the device is idle. But this isn't happening in my case even though I have ComponentName
. At this moment, to stop alarm, I have to open the app again and OFF the toggle button. (1) How could I solve it?
As I said, once the app is closed/ or minimized, I need to reopen the app to stop alarm. But if I press the toggle button to stop, it doesn't stop the alarm. The more I toggle, the more it loops through the same ringtone and speeds up the tone, that is very very wired. (2) Why it's happening and how can I solve it? [NOTE: It doesn't occur when the app is already open, this only happens when I close/minimize/or go back.]
I have used set
method. The problem with this is the alarm can also trigger in the past time set. But, this something I defenitely don't want. (3) What can be an alternative method for set()
that can solve this issue?
[P.S. I KNOW THIS IS SUCH A BIG POST, CAN BE IRRITATING FOR SOMEONE. MY HUMBLE REQUEST, DON'T COMMENT NEGATIVE OR DOWNVOTE MY POST. IF YOU REALLY DON'T LIKE MY IT AND DON'T WANT TO TAKE ANY STEP TO HELP ME, SIMPLY JUST AVOID THE POST.I AM GETTING SO MUCH FRUSTRATION AND DIPRESSION WITH THIS ALARM CLOCK APP.]
MainActivity:
package com.mycompany.alarmclock;
import android.support.v7.app.ActionBarActivity;
//imported stuff isn't shown here.
public class MainActivity extends Activity {
AlarmManager alarmManager;
private PendingIntent pendingIntent;//an action to be performed by other/foreign application
private TimePicker alarmTimePicker;//In where the user set the time
private static MainActivity inst;
private TextView alarmTextView;//the area where alarm message/notification will be displayed
public static MainActivity instance() {
return inst;
}
public void onStart() {
super.onStart();
inst = this;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
alarmTimePicker = (TimePicker) findViewById(R.id.alarmTimePicker);
alarmTextView = (TextView) findViewById(R.id.alarmText);
ToggleButton alarmToggle = (ToggleButton) findViewById(R.id.alarmToggle);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
}
public void onToggleClicked(View view) {
if (((ToggleButton) view).isChecked()) {//if toggle button is "ON" do the alarming function
Log.d("MainActivity", "Alarm On");
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, alarmTimePicker.getCurrentHour());
calendar.set(Calendar.MINUTE, alarmTimePicker.getCurrentMinute());
Intent myIntent = new Intent(MainActivity.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, 0);
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), pendingIntent);
} else {
AlarmReceiver.stopRingtone();
alarmManager.cancel(pendingIntent);
setAlarmText("");
Log.d("MyActivity", "Alarm Off");
}
}
public void setAlarmText(String alarmText) {
alarmTextView.setText(alarmText);
}
}
AlarmReceiver:
package com.mycompany.alarmclock;
import android.support.v4.content.WakefulBroadcastReceiver;
//Imported stuff isn't shown here
public class AlarmReceiver extends WakefulBroadcastReceiver {
private static Ringtone mRingtone=null;
@Override
public void onReceive(final Context context, Intent intent) {
MainActivity inst=MainActivity.instance();
inst.setAlarmText("\n Alarm!\n Get Up! Get up!");
Uri alarmUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
if (alarmUri == null) {
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
}
Ringtone ringtone = RingtoneManager.getRingtone(context, alarmUri);
mRingtone=RingtoneManager.getRingtone(context,alarmUri);
mRingtone.play();
//ringtone.play();
ComponentName comp = new ComponentName(context.getPackageName(),
AlarmService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
public static void stopRingtone(){
mRingtone.stop();
}
}
AlarmService:
package com.mycompany.alarmclock;
//imported stuff isn't shown here.
import android.support.v4.app.NotificationCompat;
public class AlarmService extends IntentService {
private NotificationManager alarmnotificationManager;
public AlarmService(){
super("AlarmService");
}
@Override
protected void onHandleIntent(Intent intent) {
sendNotification("Get up! Get up!");
}
private void sendNotification(String msg){
Log.d("AlarmService","Sending notification...:"+msg);
alarmnotificationManager=(NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
NotificationCompat.Builder alamNotificationBuilder = new NotificationCompat.Builder(
this).setContentTitle("Alarm").setSmallIcon(R.drawable.ic_launcher)
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setContentText(msg);
alamNotificationBuilder.setContentIntent(contentIntent);
alarmnotificationManager.notify(1, alamNotificationBuilder.build());
Log.d("AlarmService", "Notification sent.");
}
}
activity_mail.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
android:background="#deb887">
<TimePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/alarmTimePicker"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="#8ffdba" />
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Alarm On/Off"
android:id="@+id/alarmToggle"
android:layout_centerHorizontal="true"
android:layout_below="@+id/alarmTimePicker"
android:onClick="onToggleClicked"
android:checked="false"
android:background="#000000"
android:focusable="true"
android:textColor="#f3f6ff"
android:textSize="25dp"
android:textStyle="bold"
android:layout_marginTop="15dp" />
<TextView
android:layout_width="258dp"
android:layout_height="match_parent"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text=""
android:id="@+id/alarmText"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_below="@+id/alarmToggle"
android:background="#0e8146"
android:layout_marginBottom="80dp"
android:textColor="#ff0000" />
<Button
style="?android:attr/buttonStyleSmall"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:text="Share On Facebook"
android:id="@+id/button"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="#3B5998"
android:layout_marginBottom="10dp"
android:focusable="true"
android:shadowColor="#b0e0e6"
android:textColor="#00ff80"
android:textStyle="bold" />
</RelativeLayout>
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany.alarmclock" >
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:allowBackup="true"
android:icon="@drawable/icon_updated"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="AlarmReceiver"/>
</application>
</manifest>
UPDATE: LOG:
03-23 15:28:46.372 1474-1474/com.mycompany.alarmclock I/System.out﹕ debugger has settled (1321)
03-23 15:28:46.548 1474-1474/com.mycompany.alarmclock D/libEGL﹕ loaded /system/lib/egl/libEGL_genymotion.so
03-23 15:28:46.548 1474-1474/com.mycompany.alarmclock D/﹕ HostConnection::get() New Host Connection established 0xb7ddaf88, tid 1474
03-23 15:28:46.560 1474-1474/com.mycompany.alarmclock D/libEGL﹕ loaded /system/lib/egl/libGLESv1_CM_genymotion.so
03-23 15:28:46.560 1474-1474/com.mycompany.alarmclock D/libEGL﹕ loaded /system/lib/egl/libGLESv2_genymotion.so
03-23 15:28:46.604 1474-1474/com.mycompany.alarmclock W/EGL_genymotion﹕ eglSurfaceAttrib not implemented
03-23 15:28:46.608 1474-1474/com.mycompany.alarmclock E/OpenGLRenderer﹕ Getting MAX_TEXTURE_SIZE from GradienCache
03-23 15:28:46.608 1474-1474/com.mycompany.alarmclock E/OpenGLRenderer﹕ MAX_TEXTURE_SIZE: 8192
03-23 15:28:46.616 1474-1474/com.mycompany.alarmclock E/OpenGLRenderer﹕ Getting MAX_TEXTURE_SIZE from Caches::initConstraints()
03-23 15:28:46.616 1474-1474/com.mycompany.alarmclock E/OpenGLRenderer﹕ MAX_TEXTURE_SIZE: 8192
03-23 15:28:46.616 1474-1474/com.mycompany.alarmclock D/OpenGLRenderer﹕ Enabling debug mode 0
Upvotes: 0
Views: 252
Reputation: 6215
I want to assist you in issue #2. I believe you said the toggle button works to start and stop the Alarm timer. But it does not work when you close or minimize, which I call it "undoing the backstack" to return to the screen. I hope I got the issue correct.
Now...I don't like (not comfortable) where you place the new Intent
because the Pending Intent may lose its original context, I believe. So place/move these codes below into the OnCreate from onToggleClicked(), perhaps on the bottom is fine:
Intent myIntent = new Intent(MainActivity.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, 0);
My main concern is that I want to make sure object pendingIntent is the same when you cancel(), the one coming from set(). To be clear, the codes above should not be in onToggleClicked anymore, my suggestion.
Upvotes: 1
Reputation: 43322
Question 1 and question 3 could possibly be solved by using setExact()
instead of set()
, and using RTC_WAKEUP
instead of RTC
.
See documentation for setExact and RTC_WAKEUP.
Regarding alarms set in the past, this is by design for set()
.
The documentation states:
If the stated trigger time is in the past, the alarm will be triggered immediately. If there is already an alarm for this Intent scheduled (with the equality of two intents being defined by filterEquals(Intent)), then it will be removed and replaced by this one.
Also see this post: Android AlarmManager: how to avoid to going off of past alarms
Now, for your biggest issue, question 2, it looks like it's being caused because you don't have the Intent
and PendingIntent
set up for properly canceling the alarm if the app has been closed and re-opened.
In addition, you might want to add PendingIntent.FLAG_UPDATE_CURRENT
as an extra failsafe to prevent multiple alarms from being scheduled.
See code below:
public void onToggleClicked(View view) {
if (((ToggleButton) view).isChecked()) {//if toggle button is "ON" do the alarming function
Log.d("MainActivity", "Alarm On");
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, alarmTimePicker.getCurrentHour());
calendar.set(Calendar.MINUTE, alarmTimePicker.getCurrentMinute());
Intent myIntent = new Intent(MainActivity.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
} else {
AlarmReceiver.stopRingtone();
Intent myIntent = new Intent(MainActivity.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.cancel(pendingIntent);
setAlarmText("");
Log.d("MyActivity", "Alarm Off");
}
}
Upvotes: 1