diff --git a/Projects/SweetDreamsLite/.classpath b/Projects/SweetDreamsLite/.classpath
new file mode 100644
index 0000000..207072c
--- /dev/null
+++ b/Projects/SweetDreamsLite/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Projects/SweetDreamsLite/.project b/Projects/SweetDreamsLite/.project
new file mode 100644
index 0000000..c74788a
--- /dev/null
+++ b/Projects/SweetDreamsLite/.project
@@ -0,0 +1,33 @@
+
+
+ SweetSoundsLite
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/Projects/SweetDreamsLite/AndroidManifest.xml b/Projects/SweetDreamsLite/AndroidManifest.xml
new file mode 100644
index 0000000..e73c040
--- /dev/null
+++ b/Projects/SweetDreamsLite/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/ChangeLog b/Projects/SweetDreamsLite/ChangeLog
new file mode 100644
index 0000000..e69de29
diff --git a/Projects/SweetDreamsLite/default.properties b/Projects/SweetDreamsLite/default.properties
new file mode 100644
index 0000000..0b9250e
--- /dev/null
+++ b/Projects/SweetDreamsLite/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-8
diff --git a/Projects/SweetDreamsLite/libs/admob-sdk-android.jar b/Projects/SweetDreamsLite/libs/admob-sdk-android.jar
new file mode 100644
index 0000000..e186671
Binary files /dev/null and b/Projects/SweetDreamsLite/libs/admob-sdk-android.jar differ
diff --git a/Projects/SweetDreamsLite/res/drawable/pasusenormal.png b/Projects/SweetDreamsLite/res/drawable/pasusenormal.png
new file mode 100644
index 0000000..0d9d870
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/pasusenormal.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/pause_button.xml b/Projects/SweetDreamsLite/res/drawable/pause_button.xml
new file mode 100644
index 0000000..5e2bb38
--- /dev/null
+++ b/Projects/SweetDreamsLite/res/drawable/pause_button.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/res/drawable/pausedisabled.png b/Projects/SweetDreamsLite/res/drawable/pausedisabled.png
new file mode 100644
index 0000000..93b493f
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/pausedisabled.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/pausehot.png b/Projects/SweetDreamsLite/res/drawable/pausehot.png
new file mode 100644
index 0000000..b7f277e
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/pausehot.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/pausenormalred.png b/Projects/SweetDreamsLite/res/drawable/pausenormalred.png
new file mode 100644
index 0000000..4c9fe16
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/pausenormalred.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/pausepressed.png b/Projects/SweetDreamsLite/res/drawable/pausepressed.png
new file mode 100644
index 0000000..515ae97
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/pausepressed.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/play_button.xml b/Projects/SweetDreamsLite/res/drawable/play_button.xml
new file mode 100644
index 0000000..db6e291
--- /dev/null
+++ b/Projects/SweetDreamsLite/res/drawable/play_button.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/res/drawable/playdisabled.png b/Projects/SweetDreamsLite/res/drawable/playdisabled.png
new file mode 100644
index 0000000..ea21cda
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/playdisabled.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/playhot.png b/Projects/SweetDreamsLite/res/drawable/playhot.png
new file mode 100644
index 0000000..bb665c8
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/playhot.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/playnormal.png b/Projects/SweetDreamsLite/res/drawable/playnormal.png
new file mode 100644
index 0000000..8249cd9
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/playnormal.png differ
diff --git a/Projects/SweetDreamsLite/res/drawable/playpressed.png b/Projects/SweetDreamsLite/res/drawable/playpressed.png
new file mode 100644
index 0000000..93aca27
Binary files /dev/null and b/Projects/SweetDreamsLite/res/drawable/playpressed.png differ
diff --git a/Projects/SweetDreamsLite/res/layout/main.xml b/Projects/SweetDreamsLite/res/layout/main.xml
new file mode 100644
index 0000000..291bef8
--- /dev/null
+++ b/Projects/SweetDreamsLite/res/layout/main.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Projects/SweetDreamsLite/res/raw/ac.mp3 b/Projects/SweetDreamsLite/res/raw/ac.mp3
new file mode 100644
index 0000000..9f54d88
Binary files /dev/null and b/Projects/SweetDreamsLite/res/raw/ac.mp3 differ
diff --git a/Projects/SweetDreamsLite/res/raw/beach.mp3 b/Projects/SweetDreamsLite/res/raw/beach.mp3
new file mode 100644
index 0000000..ef5a85a
Binary files /dev/null and b/Projects/SweetDreamsLite/res/raw/beach.mp3 differ
diff --git a/Projects/SweetDreamsLite/res/raw/crickets.mp3 b/Projects/SweetDreamsLite/res/raw/crickets.mp3
new file mode 100644
index 0000000..8facaca
Binary files /dev/null and b/Projects/SweetDreamsLite/res/raw/crickets.mp3 differ
diff --git a/Projects/SweetDreamsLite/res/raw/falls.mp3 b/Projects/SweetDreamsLite/res/raw/falls.mp3
new file mode 100644
index 0000000..acc0cb8
Binary files /dev/null and b/Projects/SweetDreamsLite/res/raw/falls.mp3 differ
diff --git a/Projects/SweetDreamsLite/res/raw/river.mp3 b/Projects/SweetDreamsLite/res/raw/river.mp3
new file mode 100644
index 0000000..dd6eb50
Binary files /dev/null and b/Projects/SweetDreamsLite/res/raw/river.mp3 differ
diff --git a/Projects/SweetDreamsLite/res/values/attrs.xml b/Projects/SweetDreamsLite/res/values/attrs.xml
new file mode 100644
index 0000000..68a68fc
--- /dev/null
+++ b/Projects/SweetDreamsLite/res/values/attrs.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/res/values/sounds.xml b/Projects/SweetDreamsLite/res/values/sounds.xml
new file mode 100644
index 0000000..65da885
--- /dev/null
+++ b/Projects/SweetDreamsLite/res/values/sounds.xml
@@ -0,0 +1,10 @@
+
+
+
+ - Rainy River
+ - Beach
+ - Crickets
+ - Water Falls
+ - Air Conditioner
+
+
diff --git a/Projects/SweetDreamsLite/res/values/strings.xml b/Projects/SweetDreamsLite/res/values/strings.xml
new file mode 100644
index 0000000..572bbd5
--- /dev/null
+++ b/Projects/SweetDreamsLite/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ Hello World, WhiteNoise!
+ Sweet Sounds
+
diff --git a/Projects/SweetDreamsLite/res/xml/settings.xml b/Projects/SweetDreamsLite/res/xml/settings.xml
new file mode 100644
index 0000000..571760e
--- /dev/null
+++ b/Projects/SweetDreamsLite/res/xml/settings.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/PostMortemReportExceptionHandler.java b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/PostMortemReportExceptionHandler.java
new file mode 100644
index 0000000..ea87ba4
--- /dev/null
+++ b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/PostMortemReportExceptionHandler.java
@@ -0,0 +1,215 @@
+/**
+ * @author Twenty Codes
+ * @author ricky barrette
+ */
+package com.TwentyCodes.android.SweetSoundsLite;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Field;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+
+/**
+ * dont forget the manifest tag
+ *
+ * @author ricky
+ */
+public class PostMortemReportExceptionHandler implements UncaughtExceptionHandler, Runnable {
+ public static final String ExceptionReportFilename = "postmortem.trace";
+
+ private static final String MSG_SUBJECT_TAG = "Exception Report"; //"app title + this tag" = email subject
+ private static final String MSG_SENDTO = "twentycodes@gmail.com"; //email will be sent to this account
+ //the following may be something you wish to consider localizing
+ private static final String MSG_BODY = "Just click send to help make this application better. "+
+ "No personal information is being sent (you can check by reading the rest of the email).";
+
+ private Thread.UncaughtExceptionHandler mDefaultUEH;
+ private Activity mApp = null;
+
+ public PostMortemReportExceptionHandler(Activity aApp) {
+ mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
+ mApp = aApp;
+ }
+
+ public void uncaughtException(Thread t, Throwable e) {
+ submit(e);
+ //do not forget to pass this exception through up the chain
+ mDefaultUEH.uncaughtException(t,e);
+ }
+
+ public String getDebugReport(Throwable aException) {
+
+// NumberFormat theFormatter = new DecimalFormat("#0.");
+ //stack trace
+ StackTraceElement[] theStackTrace = aException.getStackTrace();
+
+ StringBuffer report = new StringBuffer();
+
+ report.append("--------- Application ---------\n\n");
+
+ report.append(mApp.getPackageName()+" generated the following exception:\n\n");
+
+ report.append(aException.toString() + "\n\n");
+
+ report.append("-------------------------------\n\n");
+
+ report.append("--------- Stack trace ---------\n\n");
+ for (int i = 0; i < theStackTrace.length; i++) {
+ report.append(" " + theStackTrace[i].toString() + "\n");
+ }
+ report.append("-------------------------------\n\n");
+
+ //app environment
+ PackageManager pm = mApp.getPackageManager();
+ PackageInfo pi;
+ try {
+ pi = pm.getPackageInfo(mApp.getPackageName(), 0);
+ } catch (NameNotFoundException eNnf) {
+ //doubt this will ever run since we want info about our own package
+ pi = new PackageInfo();
+ pi.versionName = "unknown";
+ pi.versionCode = 69;
+ }
+
+ Date theDate = new Date();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss_zzz");
+ report.append("-------- Environment --------\n");
+ report.append("Time\t="+sdf.format(theDate)+"\n");
+ report.append("Device\t="+Build.FINGERPRINT+"\n");
+ try {
+ Field theMfrField = Build.class.getField("MANUFACTURER");
+ report.append("Make\t="+theMfrField.get(null)+"\n");
+ } catch (SecurityException e) {
+ } catch (NoSuchFieldException e) {
+ } catch (IllegalArgumentException e) {
+ } catch (IllegalAccessException e) {
+ }
+ report.append("Device: " + Build.DEVICE + "\n");
+ report.append("Brand: " + Build.BRAND + "\n");
+ report.append("Model: "+Build.MODEL+"\n");
+ report.append("Product: "+Build.PRODUCT+"\n");
+ report.append("App:\t "+mApp.getPackageName()+", version "+pi.versionName+" (build "+pi.versionCode+")\n");
+ report.append("Locale: "+mApp.getResources().getConfiguration().locale.getDisplayName()+"\n");
+ report.append("-----------------------------\n\n");
+
+ report.append("--------- Firmware ---------\n\n");
+ report.append("SDK: " + Build.VERSION.SDK + "\n");
+ report.append("Release: " + Build.VERSION.RELEASE + "\n");
+ report.append("Incremental: " + Build.VERSION.INCREMENTAL + "\n");
+ report.append("Build Id: " + Build.ID + "\n");
+ report.append("-------------------------------\n\n");
+
+ // If the exception was thrown in a background thread inside
+ // AsyncTask, then the actual exception can be found with getCause
+ report.append("--------- Cause ---------\n\n");
+ Throwable cause = aException.getCause();
+ if (cause != null) {
+ report.append(cause.toString() + "\n\n");
+ theStackTrace = cause.getStackTrace();
+ for (int i = 0; i < theStackTrace.length; i++) {
+ report.append(" " + theStackTrace[i].toString() + "\n");
+ }
+ }
+ report.append("-------------------------------\n\n");
+
+ report.append("--------- Complete Logcat ---------\n\n");
+ report.append(getLog().toString());
+ report.append("-------------------------------\n\n");
+
+ report.append("END REPORT");
+
+ return report.toString();
+ }
+
+ protected void saveDebugReport(String aReport) {
+ //save report to file
+ try {
+ FileOutputStream theFile = mApp.openFileOutput(ExceptionReportFilename, Context.MODE_PRIVATE);
+ theFile.write(aReport.getBytes());
+ theFile.close();
+ } catch(IOException ioe) {
+ //error during error report needs to be ignored, do not wish to start infinite loop
+ }
+ }
+
+ public void sendDebugReportToAuthor() {
+ String theLine = "";
+ StringBuffer theTrace = new StringBuffer();
+ try {
+ BufferedReader theReader = new BufferedReader(
+ new InputStreamReader(mApp.openFileInput(ExceptionReportFilename)));
+ while ((theLine = theReader.readLine())!=null) {
+ theTrace.append(theLine+"\n");
+ }
+ if (sendDebugReportToAuthor(theTrace.toString())) {
+ mApp.deleteFile(ExceptionReportFilename);
+ }
+ } catch (FileNotFoundException eFnf) {
+ // nothing to do
+ } catch(IOException eIo) {
+ // not going to report
+ }
+ }
+
+ public Boolean sendDebugReportToAuthor(String aReport) {
+ if (aReport!=null) {
+ Intent theIntent = new Intent(Intent.ACTION_SEND);
+ String theSubject = mApp.getTitle()+" "+MSG_SUBJECT_TAG;
+ String theBody = "\n"+MSG_BODY+"\n\n"+aReport+"\n\n";
+ theIntent.putExtra(Intent.EXTRA_EMAIL,new String[] {MSG_SENDTO});
+ theIntent.putExtra(Intent.EXTRA_TEXT, theBody);
+ theIntent.putExtra(Intent.EXTRA_SUBJECT, theSubject);
+ theIntent.setType("message/rfc822");
+ Boolean hasSendRecipients = (mApp.getPackageManager().queryIntentActivities(theIntent,0).size()>0);
+ if (hasSendRecipients) {
+ mApp.startActivity(theIntent);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public void run() {
+ sendDebugReportToAuthor();
+ }
+
+ public void submit(Throwable e) {
+ String theErrReport = getDebugReport(e);
+ saveDebugReport(theErrReport);
+ //try to send file contents via email (need to do so via the UI thread)
+ mApp.runOnUiThread(this);
+ }
+
+ protected StringBuilder getLog(){
+ final StringBuilder log = new StringBuilder();
+ try{
+ Process process = Runtime.getRuntime().exec("logcat -d");
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+
+ String line;
+ while ((line = bufferedReader.readLine()) != null){
+ log.append(line);
+ log.append("\n");
+ }
+ }
+ catch (IOException e){
+ }
+ return log;
+ }
+}
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/Settings.java b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/Settings.java
new file mode 100644
index 0000000..e7adcf8
--- /dev/null
+++ b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/Settings.java
@@ -0,0 +1,91 @@
+/**
+ * @author Twenty Codes
+ * @author ricky barrette
+ */
+package com.TwentyCodes.android.SweetSoundsLite;
+
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.Preference.OnPreferenceChangeListener;
+
+/**
+ * this is a settings activity for WhiteNoise. it handles user changeable settings and saves them
+ * @author ricky barrette
+ */
+public class Settings extends PreferenceActivity implements OnPreferenceChangeListener {
+
+ //shared_prefs file name
+ protected static final String SETTINGS = "settings";
+
+ //the following strings are for sound track selection
+ protected static final String SOUNDS = "sounds";
+ protected static final String SOUND_RIVER = "Rainy River";
+ protected static final String SOUND_BEACH = "Beach";
+ protected static final String SOUND_CRICKETS = "Crickets";
+ protected static final String SOUND_FALLS = "Water Falls";
+ protected static final String SOUND_AC = "Air Conditioner";
+
+ //the following strings are for saving volume level from the audiomanager.STREAM_MUSIC so we can restore them back when the app quits
+ protected static final String MUSIC_VOLUME = "music_volume";
+
+ //the following strings are for timer preference
+ protected static final String TIMER_ENABLED = "timer_enabled";
+ protected static final String TIMER_LENGTH = "timer_length";
+ protected static final String TIMER_EXIT_ON_FINISH = "timer_exit_on_finish";
+
+ /**
+ * Called when the activity is first created.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ //set shared_prefs name
+ getPreferenceManager().setSharedPreferencesName(SETTINGS);
+
+ //load preferences xml
+ this.addPreferencesFromResource(R.xml.settings);
+
+ //get shared_prefs
+ SharedPreferences settings = getPreferenceManager().getSharedPreferences();
+
+ /*
+ * Initialize preference sound
+ * set OnPreferenceChangeListener
+ * and set summary to current settings
+ */
+ Preference pSounds = findPreference(SOUNDS);
+ pSounds.setOnPreferenceChangeListener(this);
+ pSounds.setSummary(settings.getString(SOUNDS, SOUND_RIVER));
+
+ /*
+ * Initialize preference timerLength
+ * set OnPreferenceChangeListener
+ * and set summary to current settings
+ */
+ Preference timerLength = findPreference(TIMER_LENGTH);
+ timerLength.setOnPreferenceChangeListener(this);
+ try {
+ timerLength.setSummary(settings.getInt(TIMER_LENGTH, 5));
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * called when a preference is changed
+ * @param preference
+ * @param newValue
+ * @return
+ * @author ricky barrette
+ */
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ preference.setSummary(newValue.toString());
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/SweetSounds.java b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/SweetSounds.java
new file mode 100644
index 0000000..a050276
--- /dev/null
+++ b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/SweetSounds.java
@@ -0,0 +1,413 @@
+/**
+ * @author Twenty Codes
+ * @author ricky barrette
+ */
+package com.TwentyCodes.android.SweetSoundsLite;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+/**
+ * this class is responsible for setting up the ui and handling ui events
+ * @author ricky
+ */
+public class SweetSounds extends Activity implements OnClickListener, OnSeekBarChangeListener{
+
+ private boolean isPlaying;
+ private ImageButton play_pause;
+ private AudioManager mAudioManager;
+ private final int STREAM = AudioManager.STREAM_MUSIC;
+ protected static MediaPlayer mp;
+ private PostMortemReportExceptionHandler mDamageReport;
+ private final int SETTINGS = Menu.FIRST;
+ private final int QUIT = Menu.FIRST +1;
+ private SharedPreferences settings;
+ protected static final String TAG = "WhiteNoise";
+ private Timer timer;
+ protected static TextView timeLeft;
+
+ /**
+ * adjusts the provided Stream volume to the provided level
+ * @param stream
+ * @param level
+ * @author ricky barrette
+ */
+ private void adjustVolume(int stream, int level) {
+
+ /*
+ * if the seek bar is set to a value that is higher than what the the stream value is set for
+ * then subtract the seek bar's value from the current volume of the stream, and then
+ * raise the stream by that many times
+ */
+ if (level > mAudioManager.getStreamVolume(stream)) {
+ int adjust = level - mAudioManager.getStreamVolume(stream);
+ for (int i = 0; i < adjust; i++) {
+ mAudioManager.adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE, stream, AudioManager.FLAG_VIBRATE);
+ }
+ }
+
+ /*
+ * if the seek bar is set to a value that is lower than what the the stream value is set for
+ * then subtract the current volume of the stream from the seek bar's value, and then
+ * lower the stream by that many times
+ */
+ if (level < mAudioManager.getStreamVolume(stream)) {
+ int adjust = mAudioManager.getStreamVolume(stream) - level;
+ for (int i = 0; i < adjust; i++) {
+ mAudioManager.adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER, stream, AudioManager.FLAG_VIBRATE);
+ }
+ }
+ }
+
+ /**
+ * loads the sound from shared_prefs that the user wants, and set the mediaplayer to loop
+ * loads river sound track by default.
+ *
+ * @author ricky barrette
+ */
+ private void loadSound(){
+ Log.i(TAG,"loadSound()");
+ try {
+ mp.reset();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ String sound = settings.getString("sounds", Settings.SOUND_RIVER);
+ Log.v(TAG,"sound = "+ sound);
+ if (sound != null){
+ if (sound.equals(Settings.SOUND_BEACH)){
+ mp = MediaPlayer.create(this, R.raw.beach);
+ mp.setLooping(true);
+ }
+ if (sound.equals(Settings.SOUND_AC)){
+ mp = MediaPlayer.create(this, R.raw.ac);
+ mp.setLooping(true);
+ }
+ if (sound.equals(Settings.SOUND_CRICKETS)){
+ mp = MediaPlayer.create(this, R.raw.crickets);
+ mp.setLooping(true);
+ }
+ if (sound.equals(Settings.SOUND_FALLS)){
+ mp = MediaPlayer.create(this, R.raw.falls);
+ mp.setLooping(true);
+ }
+ if (sound.equals(Settings.SOUND_RIVER)){
+ mp = MediaPlayer.create(this, R.raw.river);
+ mp.setLooping(true);
+ }
+ }
+ }
+
+ /**
+ * called when the user clicks on a view that has been registered with this onClickListener
+ * @author ricky barrette
+ */
+ @Override
+ public void onClick(View v) {
+ /*
+ * if the sound track is no playing then
+ * change the button background from a play symbol to a pause symbol, set isPlaying to true, load the user preferred sound track, and play it
+ * else
+ * change the button background from a pause symbol to a play symbol, set isPlaying to false, and stop playing the sound track
+ */
+ if (!isPlaying){
+ play_pause.setBackgroundDrawable(getResources().getDrawable(R.drawable.pause_button));
+ isPlaying = true;
+ loadSound();
+ mp.start();
+ if (settings.getBoolean(Settings.TIMER_ENABLED, false)){
+ Log.v(TAG,"Starting timer");
+ long time = settings.getInt("timer_length_hour", 0) * 3600000;
+ time = time + (settings.getInt("timer_length_minute", 5) * 60000);
+ Log.v(TAG,"time = " + time);
+ timer = new Timer(time);
+ timer.start();
+ }
+ } else {
+ stopPlaying();
+ }
+ }
+
+ /**
+ * called when the activity is first created
+ * (non-Javadoc)
+ * @see android.app.Activity#onCreate(android.os.Bundle)
+ * @param savedInstanceState
+ * @author ricky barrette
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ Log.i(TAG,"onCreate()");
+
+ // setup crash report handler
+ mDamageReport = new PostMortemReportExceptionHandler(this);
+ mDamageReport.run();
+ Thread.setDefaultUncaughtExceptionHandler(mDamageReport);
+
+ //initialize the play_pause button and set onClickListener
+ play_pause = (ImageButton) findViewById(R.id.play_pause_button);
+ play_pause.setOnClickListener(this);
+
+ //initialize audio manager so we can control stream volumes
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+
+ //initialize the volume seekbar and set
+ SeekBar sbVolume = (SeekBar) findViewById(R.id.volume);
+ sbVolume.setOnSeekBarChangeListener(this);
+ sbVolume.setMax(mAudioManager.getStreamMaxVolume(STREAM));
+ sbVolume.setProgress(mAudioManager.getStreamVolume(STREAM));
+
+ //load shared_prefs
+ settings = getSharedPreferences(Settings.SETTINGS, 0);
+
+ loadSound();
+
+ saveStreamVolume(STREAM);
+ }
+
+ /**
+ * creates a menu
+ * @author ricky barrette 3-30-2010
+ */
+ @Override
+ public boolean onCreateOptionsMenu (Menu menu) {
+ menu.add(1, SETTINGS, 0, "Options");
+ menu.add(1, QUIT, 0, "Quit");
+ return true;
+ }
+
+ /**
+ * here we set the Stream volume back to what ever it was when the activity started
+ * (non-Javadoc)
+ * @see android.app.Activity#onDestroy()
+ * @author ricky barrette
+ */
+ @Override
+ public void onDestroy(){
+ adjustVolume(STREAM,settings.getInt(Settings.MUSIC_VOLUME, 0));
+ super.onDestroy();
+ }
+
+ /**
+ * handles menu selection
+ * @author ricky barrette 3-30-2010
+ */
+ @Override
+ public boolean onOptionsItemSelected (MenuItem item) {
+ switch (item.getItemId()){
+ case SETTINGS:
+ Intent intent = new Intent().setClass(this, Settings.class);
+ startActivity(intent);
+ stopPlaying();
+ return true;
+
+ case QUIT:
+ stopPlaying();
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onPause(){
+ super.onPause();
+ }
+
+ /**
+ * called when the seekbar progress is changed
+ * @param seekbar that was changed
+ * @param progress of the seekbar
+ * @param fromUser
+ */
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ /*
+ * if the change was cause by the user then
+ * set the output stream the the desired level
+ */
+ if(fromUser){
+ adjustVolume(STREAM, progress);
+ }
+ }
+
+ @Override
+ public void onRestart(){
+ super.onRestart();
+ }
+
+ /**
+ * we load the track title textview in onResume so it will update every time the activity loads
+ * (non-Javadoc)
+ * @see android.app.Activity#onResume()
+ * @author ricky barrette
+ */
+ @Override
+ public void onResume(){
+ super.onResume();
+ TextView trackTitle = (TextView) findViewById(R.id.track_title);
+ trackTitle.setText(settings.getString(Settings.SOUNDS,"Rainy River"));
+ }
+
+ @Override
+ public void onStart(){
+ super.onStart();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ /*
+ * NOT YET IMPLEMENTED
+ * needed for OnSeekBarChangeListener
+ */
+ }
+
+ /**
+ * we stop the music from playing and release the media player
+ * (non-Javadoc)
+ * @see android.app.Activity#onStop()
+ * @author ricky barrette
+ */
+ @Override
+ public void onStop(){
+ stopPlaying();
+ try {
+ mp.release();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ super.onStop();
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ /*
+ * NOT YET IMPLEMENTED
+ * needed for OnSeekBarChangeListener
+ */
+ }
+
+ /**
+ * saves the current volume levels for the supplied audio stream
+ * @param stream to be saved
+ * @return true id save was successful
+ * @author ricky barrette
+ */
+ private boolean saveStreamVolume(int stream){
+ Editor edit = settings.edit();
+ edit.putInt(Settings.MUSIC_VOLUME, mAudioManager.getStreamVolume(stream));
+ return edit.commit();
+ }
+
+ /**
+ * stops music from playing, sets the pay_pause button background to a play symbol, and if the time is enabled, cancels the timer
+ *
+ * @author ricky barrette
+ */
+ private void stopPlaying(){
+ play_pause.setBackgroundDrawable(getResources().getDrawable(R.drawable.play_button));
+ isPlaying = false;
+ try {
+ mp.stop();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ Log.e(TAG,"failed to stop media player");
+ }
+ if (timer != null) {
+ timer.cancel();
+ timer = null;
+ timeLeft.setText("");
+ }
+ }
+
+ /**
+ * this internal class will handle timing functions such as a timer that stops play back of a sound track & updates timeLeft textview every second
+ * @author ricky barrette
+ */
+ class Timer extends CountDownTimer {
+
+ /**
+ * creates a new count down timer that stops play back of a sound track after specified time, and
+ * updates timeLeft textview every second
+ * @param millisInFuture
+ */
+ public Timer(long millisInFuture) {
+ super(millisInFuture, 1000l);
+ Log.i(TAG,"Timer()");
+ Log.v(TAG,"millisInFuture = "+ millisInFuture);
+ timeLeft = (TextView) findViewById(R.id.time_left);
+ int hours = (int) (millisInFuture / 3600000);
+ millisInFuture = millisInFuture - (hours * 3600000);
+ int minutes = (int) ( millisInFuture / 60000);
+ int seconds = (int) (millisInFuture % 60000);
+ seconds = seconds / 1000;
+ timeLeft.setText(hours +" : "+ padTime(minutes) +" : "+ padTime(seconds));
+ }
+
+ /**
+ * stops the sound track being played by the media player
+ * (non-Javadoc)
+ * @see android.os.CountDownTimer#onFinish()
+ * @author ricky barrette
+ */
+ @Override
+ public void onFinish() {
+ Log.i(TAG,"onFinish()");
+ timeLeft.setText("");
+ stopPlaying();
+ //if the user enables exit on finish, then kill the application
+ if(settings.getBoolean(Settings.TIMER_EXIT_ON_FINISH, false)){
+ Log.v(TAG,"exit on finish enabled, calling finish()");
+ finish();
+ }
+ }
+
+ /**
+ * updates the timeLeft textView to display the current time left till sound stops
+ * @see android.os.CountDownTimer#onTick(long)
+ * @param millisUntilFinished
+ * @author ricky barrette
+ */
+ @Override
+ public void onTick(long millisUntilFinished) {
+ Log.i(TAG,"onTick()");
+ int hours = (int) (millisUntilFinished / 3600000);
+ millisUntilFinished = millisUntilFinished - (hours * 3600000);
+ int minutes = (int) ( millisUntilFinished / 60000);
+ int seconds = (int) (millisUntilFinished % 60000);
+ seconds = seconds / 1000;
+ timeLeft.setText(hours +" : "+ padTime(minutes) +" : "+ padTime(seconds));
+ }
+
+ /**
+ * convince method for formating the seconds string
+ * @param seconds
+ * @return formated string
+ * @author ricky barrette
+ */
+ private String padTime(int seconds){
+ if (seconds <= 9)
+ return "0"+ seconds;
+ return ""+ seconds;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/TimePickerPreference.java b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/TimePickerPreference.java
new file mode 100644
index 0000000..38cd0a2
--- /dev/null
+++ b/Projects/SweetDreamsLite/src/com/TwentyCodes/android/SweetSoundsLite/TimePickerPreference.java
@@ -0,0 +1,142 @@
+/**
+ * @author Twenty Codes
+ * @author ricky barrette
+ */
+package com.TwentyCodes.android.SweetSoundsLite;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Typeface;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TimePicker;
+import android.widget.TimePicker.OnTimeChangedListener;
+
+/**
+ * creates a time picker preference that saves the hour and minutes preferred as getKey()+"_hour" and getKey()+"_minute"
+ * NOTE: it might be better to save the combined hour and minutes saves as minutes (1 hour + 30 minutes would be saved as 90 minutes)
+ * @author ricky barrette
+ */
+public class TimePickerPreference extends Preference implements OnTimeChangedListener{
+
+// private TimePicker timePicker;
+ private final String TAG = "TimePickerPreference";
+ private SharedPreferences settings;
+
+ /**
+ * creates a time picker preference that saves the hour and minutes preferred as getKey()+"_hour" and getKey()+"_minute"
+ * NOTE: it might be better to save the combined hour and minutes saves as minutes (1 hour + 30 minutes would be saved as 90 minutes)
+ * @param context
+ */
+ public TimePickerPreference(Context context) {
+ super(context);
+// timePicker = new TimePicker(getContext());
+ }
+
+ /**
+ * creates a time picker preference that saves the hour and minutes preferred as getKey()+"_hour" and getKey()+"_minute"
+ * NOTE: it might be better to save the combined hour and minutes saves as minutes (1 hour + 30 minutes would be saved as 90 minutes)
+ * @param context
+ * @param attrs
+ */
+ public TimePickerPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+// timePicker = new TimePicker(getContext());
+ }
+
+ /**
+ * creates a time picker preference that saves the hour and minutes preferred as getKey()+"_hour" and getKey()+"_minute"
+ * NOTE: it might be better to save the combined hour and minutes saves as minutes (1 hour + 30 minutes would be saved as 90 minutes)
+ * @param context
+ * @param attrs
+ * @param defStyle
+ */
+ public TimePickerPreference(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+// timePicker = new TimePicker(getContext());
+ }
+
+ /**
+ * saves the current time selected by the user into the shared_prefs keys of getKey()+"_hour" and getKey()+"_minute"
+ * NOTE: it might be better to save the combined hour and minutes saves as minutes (1 hour + 30 minutes would be saved as 90 minutes)
+ * (non-Javadoc)
+ * @see android.widget.TimePicker.OnTimeChangedListener#onTimeChanged(android.widget.TimePicker, int, int)
+ * @param view
+ * @param hourOfDay
+ * @param minute
+ * @author ricky barrette
+ */
+ @Override
+ public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
+ Log.i(TAG,"onTimeChanged");
+ SharedPreferences.Editor editor = getEditor();
+ editor.putInt(getKey()+"_hour", hourOfDay);
+ editor.putInt(getKey()+"_minute", minute);
+ editor.commit();
+ }
+
+ /**
+ * creates a linear layout the contains only a textview (for title) and a time picker dialog for user input.
+ * (non-Javadoc)
+ * @see android.preference.Preference#onCreateView(android.view.ViewGroup)
+ * @param parent
+ * @return
+ * @author ricky barrette
+ */
+ @Override
+ protected View onCreateView(ViewGroup parent){
+ Log.i(TAG, "onCreateView");
+
+ /*
+ * create a vertical linear layout that width and height that wraps content
+ */
+ LinearLayout layout = new LinearLayout(getContext());
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.gravity = Gravity.CENTER;
+ layout.setPadding(15, 5, 10, 5);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ layout.removeAllViews();
+
+ /*
+ * create a textview that will be used to display the title provided in xml
+ * and add it to the lay out
+ */
+ TextView title = new TextView(getContext());
+ title.setText(getTitle());
+ title.setTextSize(18);
+ title.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
+ title.setGravity(Gravity.LEFT);
+ title.setLayoutParams(params);
+
+ /*
+ * add the time picker to the layout
+ * set the time picker to be 24 hour style
+ * load saved time preference from shared_prefs, then set
+ * onTimeChangedListener
+ */
+ TimePicker timePicker = new TimePicker(getContext());
+ timePicker.setLayoutParams(params);
+ timePicker.setIs24HourView(true);
+ settings = getSharedPreferences();
+ timePicker.setCurrentHour(settings.getInt(getKey() + "_hour", 0));
+ timePicker.setCurrentMinute(settings.getInt(getKey() + "_minute", 5));
+ timePicker.setOnTimeChangedListener(this);
+
+ /*
+ * add the title and the time picker views to the layout
+ */
+ layout.addView(title);
+ layout.addView(timePicker);
+ layout.setId(android.R.id.widget_frame);
+
+ return layout;
+ }
+
+}