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; + } + +}