Friday, August 27, 2010

DonnsAlarm v1.2 Update

Well it didn't take me long at all. I staved off the Arduino for a bit and got the previously mentioned items taken care of. I am only linking to the source code files. I lost the previous version since I don't pay for the version control, or use any at home, and my previous posts all now reflect the current documents.

*Modified in v1.2
DonnsAlarm.java*
AlarmReceiver.java
AlarmActivity.java*
AndroidManifest.xml
alarm.xml
alarmclock.xml
DonnsAlarmProject.apk

Basically, I updated the management of the Activity settings as well as disabled the time picker when the alarm is enabled. Once I figured out the state transition, I was able to use the appropriate state event, onPause() to store all settings and the Create/Restore events to restore any saved values or set to default.

At this point, there are other features that might be fun to add, but I think the regular development on this app has ended. So here is possibly the final posting for my Simple Alarm Clock. I hope others will find it as simple, yet perfectly effective for their needs as I have.

I also linked the apk file for download and install. Make sure you have configured your Application settings to allow "Unknown sources" to allow this app to install.

Thursday, August 26, 2010

DonnsAlarm v1.1

This will hopefully be a quickly updated release, but there were some significant improvements so I figured I should at least post this. Plus I found what appears to be a great site for posting/storing my code online for free(box.net).

The alarm was working really pretty well for me, but only because some of the issues were related to uses that I originally never intended to use it for and therefore never tested. Discovered a couple combinations of current time and setting the alarm time that would cause the alarm to occur immediately instead of when I intended. I also realized I was doing some stupid comparisons for managing the two times, current and alarm. I removed some code basically with the change in technique. The initial technique was very brute force, this version is much more simple, elegant and more like how the time was intended to be handled.

So I have embedded the code and the main driver class as that code was the one that changed the most. I have linked the rest of the files for reference as well as to the final APK.

There are still a couple known issues/features to add with this release which is why I hope to follow up in a couple days with a v1.2.

The Known Issues/Features to Add:
Bug - Backing out of Main Activity, using the back button, resets all user preferences. When activity reloads, the "Enabled" check box is cleared after enabling it previously. The alarm stays scheduled and will occur. It can be disabled by enabling and then disabling an alarm.

Feature - Disabling the Time Picker Hour/Minute/AMPM controls when an Alarm is Enabled


DonnsAlarm.java


DonnsAlarm.java (This is the Main Driver Class)
AlarmReceiver.java (This receives the alarm)
AlarmActivity.java (This is for the alarm.xml layout)
alarm.xml (This is the layout for the alarm when it occurs.)
alarmclock.xml (This is the main layout)
AndroidManifest.xml (This is the applications manifest file)

Sadly, I will be distracted with my Arduino's Ethernet Shield that just arrived and I am also expecting my TTL=>RS232 converter soon. I will be writing sketches for the new hardware, but I will try to work in some more testing time and at least another update as soon as possible.

Friday, August 13, 2010

Alarm Clock 1.0 Initial Code Release

So I cleaned out all the crap comments left over in the code and put in meaningful comments throughout. I still need to go back and review the structure and make any necessary improvements. I have created a database for logging my Bug/Feature/Wish list and will knock a few down, I imagine, before I lose interest in this app for a new one.

Either way, the java code is shown below. Feel free to comment on the code and make any suggestions for improvements. I am sure advice would allow me to get this cleaned up much quicker than having to figure it all out on my own.

It will probably be best to copy paste into another text editor for the best readability.


DonnsAlarm.java

package donnomalley.alarm;

import android.app.Activity;
import android.os.Bundle;
import android.widget.*;
import android.view.View;
import android.content.Intent;
import android.app.PendingIntent;
import android.app.AlarmManager;
import java.util.Calendar;
import android.content.SharedPreferences;

/** Main Class for setting the Alarm */
public class DonnsAlarm extends Activity {

public static final String PREFS_NAME = "MyPrefsFile";
public static final String ALARM_ENABLED = "ALARM_ENABLED";
public static final String ALARM_HOUR = "ALARM_HOUR";
public static final String ALARM_MINUTE = "ALARM_MIN";
public static final String SNOOZE_MINUTE = "SNOOZE_MIN";

int AlarmHour;
int AlarmMin;
boolean AlarmEnabled;
CheckBox EnableCheckBox;
TimePicker AlarmTimePicker;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//set Layout
setContentView(R.layout.alarmclock);

//Assign UI components to local variables
EnableCheckBox = (CheckBox)findViewById(R.id.EnabledCheckBox);
AlarmTimePicker = (TimePicker)findViewById(R.id.AlarmTimePicker);
EnableCheckBox.setOnClickListener(new AlarmEnableListener());

//Fetch Saved settings (Alarm Hour, Minute, Enable) and assign values accordingly
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
AlarmEnabled = settings.getBoolean("AlarmEnabled", false);
AlarmHour = settings.getInt(ALARM_HOUR, 12);
AlarmMin = settings.getInt(ALARM_MINUTE, 0);

//Set the Time Picker's Hour, Minute, AM/PM
AlarmTimePicker.setCurrentHour(AlarmHour);
AlarmTimePicker.setCurrentMinute(AlarmMin);

//Sets the property, doesn't cause OnClick Event to fire
EnableCheckBox.setChecked(AlarmEnabled);
}

/** Called to set/cancel an alarm. */
public void SetAlarm(boolean AlarmEnabled, int AlarmHour, int AlarmMin) {
if (AlarmEnabled) {
//Build Intent/Pending Intent for setting the alarm
Intent AlarmIntent = new Intent(DonnsAlarm.this, AlarmReceiver.class);
AlarmManager AlmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent Sender = PendingIntent.getBroadcast(DonnsAlarm.this, 0, AlarmIntent, 0);

//Build Calendar object with Alarm Time and use it to set the alarm
Calendar calendar = Calendar.getInstance();
int CurHour = calendar.get(Calendar.HOUR);
int CurMin = calendar.get(Calendar.MINUTE);

calendar.set(Calendar.HOUR, AlarmHour);
calendar.set(Calendar.MINUTE, AlarmMin);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.AM_PM, Calendar.AM);
if (AlarmHour <= CurHour) { if (AlarmMin <= CurMin) { calendar.add(Calendar.HOUR, 24); } } AlmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), Sender); //Build the Strings for displaying the alarm time through Toast int CalendarHour = calendar.get(Calendar.HOUR); int CalendarMin = calendar.get(Calendar.MINUTE); String CalendarHourStr = Integer.toString(CalendarHour); String CalendarMinStr = Integer.toString(CalendarMin); if (CalendarMin < 10) { CalendarMinStr = "0" + CalendarMinStr; } String strAmPM; if (calendar.get(Calendar.AM_PM) == Calendar.AM) { strAmPM = "AM"; } else { strAmPM = "PM"; } Toast.makeText(this, "Alarm Set For " + CalendarHourStr + ":" + CalendarMinStr + " " + strAmPM, Toast.LENGTH_LONG).show(); //Save settings Enabled, Alarm Hour, Alarm Minute SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean(ALARM_ENABLED, true); editor.putInt(ALARM_HOUR, AlarmHour); editor.putInt(ALARM_MINUTE, AlarmMin); //Commit the edits editor.commit(); } else { //Build Intent/Pending Intent for canceling the alarm Intent AlarmIntent = new Intent(DonnsAlarm.this, AlarmReceiver.class); AlarmManager AlmMgr = (AlarmManager)getSystemService(ALARM_SERVICE); PendingIntent Sender = PendingIntent.getBroadcast(DonnsAlarm.this, 0, AlarmIntent, 0); AlmMgr.cancel(Sender); //Display Alarm Disabled Message Toast.makeText(DonnsAlarm.this, "Alarm Disabled", Toast.LENGTH_LONG).show(); //Save setting Enabled SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean(ALARM_ENABLED, false); // Commit the edits editor.commit(); } } /** Class for implementing the Enable's Check Box Click Event Listener */ public class AlarmEnableListener implements CheckBox.OnClickListener { @Override public void onClick(View v) { //Read State of UI components and call SetAlarm routine AlarmEnabled = EnableCheckBox.isChecked(); AlarmHour = AlarmTimePicker.getCurrentHour(); AlarmMin = AlarmTimePicker.getCurrentMinute(); SetAlarm(AlarmEnabled, AlarmHour, AlarmMin); } } } 



AlarmReceiver.java

package donnomalley.alarm;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.app.NotificationManager;
import android.app.Notification;
import android.app.PendingIntent;

/** Class for receiving broadcast when the Alarm occurs */
public class AlarmReceiver extends BroadcastReceiver
{
public static final String ALARM_ALERT_ACTION = "com.android.alarmclock.ALARM_ALERT";
public static final String ALARM_INTENT_EXTRA = "intent.extra.alarm";

@Override
public void onReceive(Context context, Intent intent) {
//Create Intent to Start the AlarmActivity "Snooze" Activity
Intent myIntent = new Intent(context, AlarmActivity.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(myIntent);

//Build pending intent from calling information to display Notification
PendingIntent Sender = PendingIntent.getBroadcast(context, 0, intent, 0);
NotificationManager manager = (NotificationManager)context.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.icon, "Wake up alarm", System.currentTimeMillis());
notification.setLatestEventInfo(context, "Donns Alarm", "WAKE UP BITCH", Sender);
notification.flags = Notification.FLAG_NO_CLEAR;
manager.notify(R.string.app_name, notification);
}
}



AlarmActivity.java

package donnomalley.alarm;

import java.util.Calendar;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.RadioButton;
//import android.widget.Toast;
import android.view.Window;
import android.view.WindowManager;

/** Class for managing the "Snooze"(AlarmActivity) Activity */
public class AlarmActivity extends Activity
{
public RadioButton SnoozeRadio1;
public RadioButton SnoozeRadio2;
public RadioButton SnoozeRadio5;
public RadioButton SnoozeRadio10;
public int SnoozeMin;
public MediaPlayer mMediaPlayer;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//Set window features to hide the notification bar and make the UI fullscreen before assigning the layout
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.alarm);

//Get Last used Snooze Value and use as default
SharedPreferences settings = getSharedPreferences(DonnsAlarm.PREFS_NAME, 0);
SnoozeMin = settings.getInt(DonnsAlarm.SNOOZE_MINUTE, 10);

//Assign UI components to local variables and define Click Event Listeners
SnoozeRadio1 = (RadioButton)findViewById(R.id.SnoozeMin1);
SnoozeRadio1.setOnClickListener(new MyRadioListener1());
SnoozeRadio2 = (RadioButton)findViewById(R.id.SnoozeMin2);
SnoozeRadio2.setOnClickListener(new MyRadioListener2());
SnoozeRadio5 = (RadioButton)findViewById(R.id.SnoozeMin5);
SnoozeRadio5.setOnClickListener(new MyRadioListener5());
SnoozeRadio10 = (RadioButton)findViewById(R.id.SnoozeMin10);
SnoozeRadio10.setOnClickListener(new MyRadioListener10());
Button SnoozeButton = (Button)this.findViewById(R.id.snooze_button);
SnoozeButton.setOnClickListener(new MySnoozeListener());

//Initialize UI Components
SnoozeRadio1.setChecked(false);
SnoozeRadio2.setChecked(false);
SnoozeRadio5.setChecked(false);
SnoozeRadio10.setChecked(false);

//Set Default Value based on saved preference
if (SnoozeMin == 1) {
SnoozeRadio1.setChecked(true);
}
else if(SnoozeMin == 2) {
SnoozeRadio2.setChecked(true);
}
else if(SnoozeMin == 5 ) {
SnoozeRadio5.setChecked(true);
}
else if(SnoozeMin == 10) {
SnoozeRadio10.setChecked(true);
}

//Create Media Player for sounding the system alarm
Uri alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
mMediaPlayer = new MediaPlayer();
try {
mMediaPlayer.setDataSource(this, alert);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mMediaPlayer.setLooping(true);
mMediaPlayer.prepare();
mMediaPlayer.start();
}
catch(Exception e) {
//TODO : Implement Error Checking
}
}

//Class for implementing the Click Event listener for the Radio Button snooze 1
public class MyRadioListener1 implements RadioButton.OnClickListener {
@Override
public void onClick(View v) {
SnoozeRadio1.setChecked(true);
SnoozeRadio2.setChecked(false);
SnoozeRadio5.setChecked(false);
SnoozeRadio10.setChecked(false);
}
}

//Class for implementing the Click Event listener for the Radio Button snooze 2
public class MyRadioListener2 implements RadioButton.OnClickListener {
@Override
public void onClick(View v) {
SnoozeRadio1.setChecked(false);
SnoozeRadio2.setChecked(true);
SnoozeRadio5.setChecked(false);
SnoozeRadio10.setChecked(false);
}
}

//Class for implementing the Click Event listener for the Radio Button snooze 5
public class MyRadioListener5 implements RadioButton.OnClickListener {
@Override
public void onClick(View v) {
SnoozeRadio1.setChecked(false);
SnoozeRadio2.setChecked(false);
SnoozeRadio5.setChecked(true);
SnoozeRadio10.setChecked(false);
}
}

//Class for implementing the Click Event listener for the Radio Button snooze 10
public class MyRadioListener10 implements RadioButton.OnClickListener {
@Override
public void onClick(View v) {
SnoozeRadio1.setChecked(false);
SnoozeRadio2.setChecked(false);
SnoozeRadio5.setChecked(false);
SnoozeRadio10.setChecked(true);
}
}

//Class for implementing the Click Event listener for the Snooze Button
public class MySnoozeListener implements Button.OnClickListener {
@Override
public void onClick(View v) {
boolean UpdatePreferences = false;

//Get AlarmEnabled Value to determine if Snooze Disable's alarm or snoozes
SharedPreferences settings = getSharedPreferences(DonnsAlarm.PREFS_NAME, 0);
boolean AlarmEnabled = settings.getBoolean(DonnsAlarm.ALARM_ENABLED, false);

//Stop Alarm Sound
try {
mMediaPlayer.stop();
}
catch(Exception e) {
//TODO : Implement Error Checking
}

//Check Alarm Enabled Preference: Enabled = Snooze Alarm, Disabled = Disarm Alarm
if (AlarmEnabled) {
//Check Snooze value and set flag to update preferences as needed
if (SnoozeRadio1.isChecked() && SnoozeMin != 1) {
SnoozeMin = 1;
UpdatePreferences = true;
} else if (SnoozeRadio2.isChecked() && SnoozeMin != 2) {
SnoozeMin = 2;
UpdatePreferences = true;
} else if (SnoozeRadio5.isChecked() && SnoozeMin != 5) {
SnoozeMin = 5;
UpdatePreferences = true;
} else if (SnoozeRadio10.isChecked() && SnoozeMin != 10) {
SnoozeMin = 10;
UpdatePreferences = true;
}

//Set Calendar Value for Snooze Alarm
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, SnoozeMin);

//Build Intent and Pending Intent to Set Snooze Alarm
Intent AlarmIntent = new Intent(AlarmActivity.this, AlarmReceiver.class);
AlarmManager AlmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent Sender = PendingIntent.getBroadcast(AlarmActivity.this, 0, AlarmIntent, 0);
AlmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), Sender);

//Update the Snooze Time Preference if flag is set
if (UpdatePreferences) {
//TODO : Save settings Enabled, Alarm Hour, Alarm Minute
SharedPreferences.Editor editor = settings.edit();
editor.putInt(DonnsAlarm.SNOOZE_MINUTE, SnoozeMin);

// Commit the edits!
editor.commit();
}
}

//Cancel the Notification. Will re-occur on next alarm occurance
NotificationManager manager = (NotificationManager)getSystemService(android.content.Context.NOTIFICATION_SERVICE);
manager.cancel(R.string.app_name);

//Close Activity
finish();
}
}

/** Destructor routine to ensure media player is cleaned up after */
@Override
protected void onDestroy() {
super.onDestroy();
//If the media player is playing stop it, and release the memory when the activity is closed.
if (mMediaPlayer != null) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
mMediaPlayer.release();
mMediaPlayer = null;
}

}
}

Thursday, August 12, 2010

Alarm Clock 1.0

I am starting this post after the completion of my Basic Alarm Clock app. I finally have an alarm clock where the Snooze button is not anywhere close to the disarm/disable button. I have disabled my alarm too many times while trying to snooze, only to wake up hours later, and often late for work.

My answer to this was to learn how to write an Android app and fill a need I had at the same time. I am calling it a 1.0 as it is the first release candidate that has satisfied all my testing requirements. I had a couple false releases, but all within about an hour and quickly went to task fixing the new issue. I finally ended up with a very simple alarm clock.

The main Activity loads and displays an analog clock with the current time, a time picker for selecting an alarm time and an "Enable" check box. This activity only enables and disables any alarms associated with the application.

When an alarm occurs, the snooze Activity is loaded. This display provides 4 different snooze radio buttons (1,2,5,10 Minutes) and a snooze button. A notification is also activated when the alarm occurs. Clicking on it will bring up the snooze Activity display. When the snooze activity is created, the media player is used to play the system alarm sound on loop and the original alarm is cancelled. Clicking the snooze button on the display will cancel the notification, stop the media player, and if the alarm is still enabled, reschedule another alarm to occur according to the snooze time selected. If the alarm was already disabled, the snooze will work only as a disable of the current alarm and doesn't schedule a new alarm.

The only way to disable the occurring alarm is to go back to the main activity display and uncheck the Enable check box. This ensures that I must first snooze the alarm to cancel the notification and sound, then go back to the main display and disable it when I am awake.

I am not attaching my source code because it is not up to my standards of code I would share quite yet. I was short with comments and have a lot of commented debug toast messages that I want to remove. Once I have the code cleaned up a bit and commented better I will be posting it as well. I am sure there will be many places for improvements and I may post a few updates as I clean up/better organize my code.

Screen Shots of the app runing on the Emulator: