Saturday, November 27, 2010

Home Automation Android Client v1.0 Release

I have completed my 1.0 release!! I thought I would be posting this earlier in the Month, but with work and life, I have not had any time to write any code. I got a nice chunk of time yesterday and a little today for final cleanup. As usual, I have posted my files on Box.net and have embedded a window for accessing everything below. I have not included any new screen shots as the changes did not really affect the way the application appears.

Regarding the changes made, most of the updates were to address user interaction and prevent the user from making incorrect choices. I cleaned up both the Room and Unit(Device) Configuration Activities. I also added a bunch of code/functionality to the Main Activity. At first, the new code broke the application. It was so bad that I was at the point of building a test project and doing more investigation online about parameter passing as I thought I was losing my values. As it turned out, my code was sound, with the exception that I forgot to assign values to my variables before attempting to use them. I also mixed up how I was managing my addresses which was causing units to be assigned to the wrong rooms. Once I realized this and was able to easily replicate the issue, I had to rewrite a bunch of code to fix it.

There are a couple of things still not addressed in this release of the application. They may appear as updates or by the 2.0 release. The application does not handle landscape mode at all. I also didn't lock the application to portrait mode as I do want to address this in a future release. Lastly, that I know of,  when switching between landscape and portrait, the view will reset itself and any changes not saved will most likely be lost.

Now I will be spending my time on my Arduino code adding the TTL to Serial Converter I soldered together. Once I have that piece finished, I will be tying it all together in it's final form. From there, it will be only a matter of adding more devices and updating the configurations to use it all. Based on the family schedule over the next month or two though, I don't expect to be done with it until well into the new year. I will just hope I am wrong and it happens earlier.

Once I am controlling all the important parts of my home that I want to start with, I will be back to work on updates and future versions. Here's to not finding any significant bugs or need for code improvements until then.

Wednesday, November 3, 2010

HomeAutomation Client Beta is Ready

As promised, I am posting the Beta today. I needed to put together the screenshots and post the source files online. As usual, the files have all been posted on box.net and the folder is embedded below. As my post last night implied, there is still some work to be done on the code, including commenting and cleanup in addition to the user error proofing. 







The Main Activity Screen is the meat and potatos of the app. Currently, the app is only designed to send commands one way. There is a response that is currently being displayed, just not used for anything yet. If nothing has been configured, the app will load and won't do anything. The digital button will toggle and the Analog slider will move, but that is about it. 











This is just a shot of the Main menu. Clicking "Exit", does just what it implies. "Config" launches a list of configuration options for selection as in the next Screen shot






These are the configuration options. "Rooms" will bring you into the room configuration activity, "Units" for configuring the devices in the rooms, and "Server" for defining the IP/Host Address of the server (Arduino with my version of the Webduino server).

This is the Room Configuration Activity Screen. The room code is a spinner with the options of 'A' through 'P' to match the X10 House Code range. The name is the user defined name to associate with the room code. I think I will be rearranging the order of the components to keep the Name field at the top.

The "DELETE ALL ROOMS" button is my way of being able to delete rooms without having to worry about the functionality of one at a time.

The buttons on the bottom are accessible through the menu. I think I prefer the menus, but I had issues with the Delete code working when implemented through the menu.


This is the Device, or Unit, Configuration activity. Similar to the Room with additional properties unique to devices. The Room Code and Device Codes support the X10 values of 'A' through 'P' and 1 through 16 respectively.

The "DELETE ALL UNITS" is just like the Room Configuration's "Delete.." button just for the configured Devices.

This screen also utilizes the Room Configuration's menu for saving/canceling. This screen is the reason I prefer the menu option for Delete as well. I think it makes the screens look cleaner. I hope to resolve the issue before the 1.0 release.
This is the activity screen for modifying the server IP Address. The IP address that is already configured will populate the "Server" text field when launched.

I have my Arduino set up on my home LAN at the address shown. I plan on exposing my Arduino to the web, but want to figure out how I will authenticate to ensure the commands come from me or somebody I have provided the necessary authentication information. This feature is definitely not going to be in the 1.0 release as I need to figure out the Arduino side first.

The "Update" button does as it suggests and updates the client to use the Server IP address in the Edit text. I didn't use the menu here as I only needed to save. The cancel can be performed by hitting the phone's back button and returning to the Main Activity.






This is the Main Activity again, but now there are items selected and the send button was pressed. The Toast message is the entire HTTP Request string as it was sent to the server. I am displaying this for my current debugging needs.


This is a response received from the Arduino. I am displaying the JSON response with a Toast Message for debugging purposes.



Overall, the functionality is still relatively simple, but it is just the beta for what will be a 1.0 release. I am pretty excited about finishing this. Once I am done with the 1.0, I have to go back to my Arduino server to finish implementing some functionality there. With the tasks left, I am feeling optimistic about my time frame for doing my actual home testing. I will continue to post project updates throughout the process.

Tuesday, November 2, 2010

HomeAutomation Client Beta v0.7: Will be Posted Tomorrow

This is just a quick status update since I put the time in to get the beta ready for release tomorrow. I added a few more functional pieces to round out everything I need for my first release. I added the Server Configuration into the stored preferences, added functionality for deleting all Rooms/Units and set the Unit Spinner to update based on the Room Selection.

I have not addressed any of the UI user interaction, in terms of eliminating, minimizing or catching usability errors. For example, the app will currently allow all the Rooms to be deleted from the configuration while there are still devices associated with them. I am still using a mix of buttons and menus throughout. I am still trying them out to see if I prefer one over the other, at least for the the different needs of this project. Once I get the beta posted tomorrow, the usability, error proofing and look/feel will be the next process to finish up. The code will be ready for a release candidate post once those issues have been addressed.

Tomorrow, I will provide a post for the beta with Screenshots, links to the code and the apk file, as well as a brief explanation of everything.

Monday, November 1, 2010

HomeAutomation Client Beta Coming Along, slowly...

Free time has been scarce and as a result, this project has been suffering. With work, Halloween, and my wedding anniversary, last week, I was very busy. But, it was a fun and exciting week since the kids really got into Halloween this year.

Anyways, I have finally made enough progress that most testing has been moved to my physical phone and off of the emulator. The Emulator doesn't have true network support, and since networking is the core of this project, it was time.
Server Configuration Activity
In regards to updates, I have added a Server Configuration Activity (left) for allowing the user to define the server IP. I have also been testing the functionality with regards to the HTTP request strings I am generating, and the JSON responses I am receiving. I am currently displaying both strings within simple Toast messages for debugging purposes, but have a plan to do something with their responses later. Most likely when they can indicate a success/failure of the command issued.

I also need to add some more UI code for managing the main Activity's Spinner controls before I stop calling this a beta. Currently the Room List and Unit List spinners are populated with every possible option. I want the Unit List to update according to the Room Selection. I have to finish testing and possibly flushing out some additional functionality for saving/restoring preferences  first, but the spinner code will be next.

I have updated my files on box.net . Here are the links to the project folder, the app apk file, as well as the individual source files updated since last post:
ArduinoClient.apk
Arduino Client(Folder)
JsonParser.java
ServerConfigurator.java
editserver.xml
menu.xml
AndroidManifest.xml

Sunday, October 24, 2010

HomeAutomation Client beta is close to release

I have the majority of the Android client app built and functioning within the emulator. There are a few finishing touches needed, but those are just minor details at this point. The initial hard work has been done and the remaining code is tedious but only requires time. At this rate, I am estimating the client's completion by next weekend. There are many more features that I want to add, but I will hold off for now so I can get back to the Arduino side of the development again. I will finally buy the X10 Serial device and make the project begin to function as a whole.

Todays update will include links to the code as well as some screenshots. As I have stated, the code isn't entirely complete, but is close enough. The code is also mostly commented.

Below are some screen shots of the updated UI. This UI is a huge improvement on my previous one where there was only one text field and one button with everything hard coded. This version doesn't have any of the hard coded components of the prototype, but also doesn't have the configuration to assign the settings by the user yet.

 I have linked the java and xml files below to the screen shots they are associated with. Here is the link to my box.net Folder with the whole project including the apk file.

Main Activity(home.xml/JsonParser)


Configuration Menu
UnitConfigurator.java / createunit.xml
Device Configuration Activity

Device Type Selections

Hopefully this week, I will complete the  initial client beta release. Once that is done, I will be back to the Arduino to tie all the pieces together. I am feeling pretty close to having part of my house fully controlled by my phone. I must admit I am excited and will probably  be burning the midnight oil to get it done quicker. To see the Arduino code/project information you can check out my Arduino Blog or the code folder I have on box.net.

Saturday, October 23, 2010

Got It!

I guess I just needed some time away from the code and some different search keywords. I identified the cause this morning, but was suspect of it last night. Since my initial round with Google didn't provide any results that were usable, I was left with two options. First was to just give up, but this is not really my style. The second option, hack and hack until it works was what I went with.


I thought my code was already stripped down to the bare essentials so I just started chopping. There were only a few lines that I knew were 100% required to implement but everything else just got cut. I was stuck with a non-functional app again. I put the few lines back one by one until it worked. As great as it was to get it functioning the way I expected, I still didn't understand what the difference was. 


I went to Google to figure out what the line did that I removed. In the top few results, I found a post with somebody having the exact same problem as well as the explanation of the issue. The basic answer is that the line, myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)caused my sub-activity to not really be a sub-activity. So as soon as the new activity was launched, it was becoming it's own application so my main activity would immediately receive the "RESULT_CANCELED" result. 


We have a bit of a busy weekend so I am not sure when I will get back to my Arduino Client, but I am hoping it will be this weekend still. Either way, the kids are getting up. I just wanted to take advantage of this extra morning time before my normal day kicks in.

Thursday, October 21, 2010

Stuck on Sub-Activities

So I have hit a wall that I can't seem to get around. I am launching a sub-activity and attempting to get a result. I have a couple weird things happening, but everything I find online tells me I am doing this correctly.

I even created a simple little test project called SubActivityTest (you can download the zip file with all the source/xml files). It simply launches a sub-activity and waits for the response and writes to a Text View. I have simplified the code to the bare minimum and not only is it NOT working, but it is also returning before the activity even launches.

My code for launching the sub-activity:
Intent myIntent = new Intent(SubActivityTest.this, SubActivity.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(myIntent, 1);

My Code for setting the result from my sub-activity:
setResult(RESULT_OK);
finish();

My Code for catching the return:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
TxtView.setText("OK Pressed");
}
else {
TxtView.setText("resultCode = " + Integer.toString(resultCode));
}
}
}

As I said before, it returns immediately and my text view updates to "resultCode = 0" and then the sub-activity opens. I set the result and finish() from the subactivity and the text view text doesn't change. It remains "resultCode = 0". I guess I am not even sure the routine is running when control returns from the sub-activity. I need this to work and will be scouring the forums to find a fix.

If anybody comes across this and can identify my mistake I would greatly appreciate it.

Tuesday, October 19, 2010

HomeAutomation Client update

I have been working on my client but have not had the time I was hoping to dedicate. Work has been busy and we have been doing extra fall activities as a family since the weather has been so nice.

At this point, I have updated the main activity look/feel. I added Spinners (Combo Box) for selecting "Rooms" and "Units", components for displaying/setting digital and analog settings as well as  a button to send the command. I also created a Room Configuration Activity. The main Activity doesn't recognize the change without restarting the app, but that will hopefully be fixed tonight. See screenshots below for the two Activities. "Rooms"/"Room Code" are synonymous for "House Code" in X10 speak. However Room/House Codes will be selected by User Defined Names for the Room instead of the bland X10 codes of A-P. Unit is modeled directly after the X10 "Unit Code" with the simple twist of a User Defined Name in place of the X10 codes of 1-16. Opening the configuration as well as saving/canceling configuration changes is done through menus(not pictured below).

Mostly this is merely a shell for the minimal functionality. The UI management code has not been started and will probably be saved for last. User error proofing is always a last priority when developing apps for myself.

My Updated UI Activities:
Main Activity
Room Configuration Activity


Monday, October 11, 2010

HomeAutomation Client v0.0.0.1

I have developed my app to the point that satisfies my needs as a proof of concept. Now that I am there, I have started outlining my plans for the 1.0 version. I will have to brush up on some more of my java, but it will be good information anyways.

I have to re-learn my options for collections of objects. In Delphi, I would use a TObjectList, TStringList or a child class that inherits from TObjectClass. However, for java, it has been long enough that I don't remember the built in constructs that are at my disposal. I could attempt to write my own version of the TObjectList class, and will if there are no other options, but have to believe I can find something first. At least, I will start with Google and go from there.

Once I am done investigating collections and a couple other minor questions, I will be flushing out my next generation of a UI. It will be an actual UI that somebody might use. Currently, the app I have linked to here is only an EditText, TextView and a button. The app simply sends an HTTP Request with the exact text in the EditText and displays the result in the TextView. This happens when the application loads as well as when the button is clicked. The server is an Arduino Micro controller on my home LAN. I currently have my router set up to forward port 80 to Arduino so I can also access it over my 3G. I have posted about it here on my blog. I also have the code for my version of the Webduino server on box.net. Any web server capable of receiving/responding to this app's requests would also work.

Sunday, October 3, 2010

A New Project (My Home Automation Client)

I have begun a new project for my Android phone. I have gotten far enough with my Arduino webserver development to need a client. I am mostly using my desktop web browser for testing/debugging, but at my last minor milestone, I needed a client to verify I could read data, from the Arduino, over my home wifi. I have taken it a step further, enabled port-forwarding on my router and now have access to my Arduino's webserver over the WAN.

The arduino ins't connected to anything yet (well anymore), it is only really echos some minor information back. It was at one point, but my learning-to-solder ruined that quickly. I fried the microchip I connected the Arduino to and that was the end of my physical X10 control. It is back to outputting text to confirm. Initially, before I had both the Arduino and Android projects to their respective milestones, I did have it controlling a circuit within my home. I used X10 sockets so I didn't have to do anything more than replace sockets. That ended when I fried the chip. The actual milestone was capable of sending the status/values of the digital and analog pins on the Arduino. I created my Android client to make a basic HTTP request, parse the results and display them as I desired within the user controls on the Activity.

The Activity was an extremely simple file. It consisted of two controls. The first was a TextView and the second was a button. The button initiated the HTTP Request and the TextView was used to display the parsed data. I used this application to test a couple techniques. Initially, I needed to confirm I could send/receive the HTTP request. This turned out to be much simpler than I expected. I found an example online quickly, tried the code, modified it to fit my needs and was done. The other technique was JSON parsing. I stumbled onto json through the Webduino demo sketch. I found it was essentially just an alternative to XML. It appears to be a lighter alternative in terms of necessary bandwidth and program memory. Fortunately, java already had a parser so I didn't have to write it myself. This also turned out to be relatively simple. I, again, found some code online, modified it to fit my needs and was done. I parsed the result of the HTTP response and selected the Analog values for display in the TextView.

I am still working on the Arduino infrastructure. I need to finish the command processor. My next milestone at the very least is to implement all the command processing options. It will still most likely echo back the request, but having each option stubbed out will be nice. It will also make it easy to go back and finish one piece at a time.

My next Android step though is to make a more functional and feature-rich client. I want to make some more settings user-definable. I also want to provide a better UI to issue some-what custom requests and display the response's results.

Hopefully the next post will be sooner rather than later. I have been busy with life in general, and it has been hard enough getting to the point I have. However, I keep pushing and will update again.

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: