OSDN Git Service

Code drop from //branches/cupcake/...@124589
authorThe Android Open Source Project <initial-contribution@android.com>
Thu, 18 Dec 2008 02:06:01 +0000 (18:06 -0800)
committerThe Android Open Source Project <initial-contribution@android.com>
Thu, 18 Dec 2008 02:06:01 +0000 (18:06 -0800)
14 files changed:
AndroidManifest.xml
res/layout-land/main.xml
res/layout-port/main.xml
res/values-de-rDE/strings.xml [deleted file]
res/values-de/strings.xml [new file with mode: 0644]
res/values-en-rGB/strings.xml
res/values-es-rUS/strings.xml [deleted file]
res/values-fr-rFR/strings.xml [deleted file]
res/values-it-rIT/strings.xml [deleted file]
res/values-ja/strings.xml [new file with mode: 0644]
res/values-zh-rTW/strings.xml [deleted file]
res/values/strings.xml
src/com/android/soundrecorder/Recorder.java
src/com/android/soundrecorder/SoundRecorder.java

index 3f535c3..0215f32 100644 (file)
@@ -18,6 +18,7 @@
     package="com.android.soundrecorder">
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
     <application android:label="@string/app_name"
                  android:icon="@drawable/ic_launcher_soundrecorder">
         <activity android:name="SoundRecorder"
@@ -28,8 +29,9 @@
             <intent-filter>
                  <action android:name="android.intent.action.GET_CONTENT" />
                  <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.OPENABLE" />
-                 <data android:mimeType="audio/*" />
+                 <category android:name="android.intent.category.OPENABLE" />
+                 <data android:mimeType="audio/amr" />
+                 <data android:mimeType="audio/3gpp" />
             </intent-filter>
             <intent-filter>
                  <action android:name="android.provider.MediaStore.RECORD_SOUND" />
index 641e5ad..05220ff 100644 (file)
@@ -21,8 +21,8 @@
             android:layout_height="wrap_content"
             android:textSize="115dip"
             android:layout_alignParentRight="true"
-                       android:layout_marginTop="-4dip"
-                       android:layout_marginRight="10dip"
+            android:layout_marginTop="-4dip"
+            android:layout_marginRight="10dip"
             style="@android:style/TextAppearance.Large" />
 
         <LinearLayout android:id="@+id/stateMessage2Layout"
@@ -31,8 +31,8 @@
             android:layout_height="wrap_content"
             android:layout_alignParentLeft="true"
             android:baselineAligned="false"
-                       android:layout_marginLeft="10dip"
-                       android:layout_marginTop="20dip">
+            android:layout_marginLeft="10dip"
+            android:layout_marginTop="20dip">
             
             <ImageView android:id="@+id/stateLED"
                 android:layout_width="wrap_content"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@+id/stateMessage2Layout"
-            android:text="@string/storage_available"
-                       android:layout_marginLeft="30dip"
+            android:layout_marginLeft="30dip"
             style="@android:style/TextAppearance.Small" />                
 
-           <ProgressBar android:id="@+id/stateProgressBar"
-               android:orientation="horizontal"
-               android:layout_width="135dip"
-               android:layout_height="wrap_content" 
-               android:max="100"
-               android:progress="0"
+        <ProgressBar android:id="@+id/stateProgressBar"
+            android:orientation="horizontal"
+            android:layout_width="135dip"
+            android:layout_height="wrap_content" 
+            android:max="100"
+            android:progress="0"
             android:layout_alignParentLeft="true"
-                       android:layout_marginLeft="10dip"
-                       android:layout_marginTop="20dip"
-               style="?android:attr/progressBarStyleHorizontal" />
+            android:layout_marginLeft="10dip"
+            android:layout_marginTop="20dip"
+            style="?android:attr/progressBarStyleHorizontal" />
 
     </RelativeLayout>
     
         android:layout_height="wrap_content"
         android:layout_weight="1">
 
-       <RelativeLayout
-               android:layout_width="fill_parent"
-               android:layout_height="fill_parent">
-
-               <LinearLayout android:id="@+id/exitButtons"
-                   android:orientation="horizontal"
-                   android:layout_width="fill_parent"
-                   android:layout_height="wrap_content" 
-                   android:layout_centerInParent="true" >
-                   
-                   <Button android:id="@+id/acceptButton"
-                       android:layout_width="fill_parent"
-                       android:layout_height="wrap_content"
-                       android:text="@string/accept"
-                       android:layout_weight="1"
-                           android:layout_marginLeft="70dip" 
-                           android:layout_marginRight="2dip" />
-           
-                   <Button android:id="@+id/discardButton"
-                       android:layout_width="fill_parent"
-                       android:layout_height="wrap_content"
-                       android:text="@string/discard"
-                       android:layout_weight="1"
-                       android:layout_marginLeft="2dip" 
-                       android:layout_marginRight="70dip" />
-               
-               </LinearLayout>
-               </RelativeLayout>
+            <LinearLayout android:id="@+id/exitButtons"
+                android:orientation="horizontal"
+                android:gravity="center_vertical"
+                android:layout_gravity="center_vertical"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content" 
+                android:layout_centerInParent="true" >
+                
+                <Button android:id="@+id/acceptButton"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/accept"
+                    android:layout_weight="1"
+                    android:layout_marginRight="2dip" 
+                    android:layout_marginLeft="50dip" />
+        
+                <Button android:id="@+id/discardButton"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/discard"
+                    android:layout_weight="1"
+                    android:layout_marginLeft="2dip" 
+                    android:layout_marginRight="50dip" />
+            
+            </LinearLayout>
     
-       <RelativeLayout
-               android:layout_width="fill_parent"
-               android:layout_height="fill_parent">
-               
-               <com.android.soundrecorder.VUMeter android:id="@+id/uvMeter"
-                   android:layout_width="150px"
-                   android:layout_height="60px"
-                   android:layout_centerInParent="true" />
-               </RelativeLayout>
+        <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent">
+            
+            <com.android.soundrecorder.VUMeter android:id="@+id/uvMeter"
+                android:layout_width="150px"
+                android:layout_height="60px"
+                android:layout_centerInParent="true" />
+        </RelativeLayout>
 
     </FrameLayout>
     
         android:gravity="center_horizontal"
         android:orientation="vertical">
 
-           <LinearLayout
-               android:layout_marginTop="4dip"
-               android:layout_marginBottom="4dip"
-           android:orientation="horizontal"
-               android:layout_width="wrap_content"
-               android:layout_height="fill_parent" >
+        <LinearLayout
+            android:layout_marginTop="4dip"
+            android:layout_marginBottom="4dip"
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent" >
 
-               <ImageButton android:id="@+id/recordButton"
-                       android:layout_height="fill_parent" 
-                               style="@android:style/MediaButton"
-                   android:src="@drawable/record" />
-       
-               <ImageButton android:id="@+id/playButton"
-                       android:layout_height="fill_parent" 
-                               style="@android:style/MediaButton"
-                   android:src="@drawable/play" />
-       
-               <ImageButton android:id="@+id/stopButton"
-                       android:layout_height="fill_parent" 
-                               style="@android:style/MediaButton"
-                   android:src="@drawable/stop" />
+            <ImageButton android:id="@+id/recordButton"
+                android:layout_height="fill_parent" 
+                style="@android:style/MediaButton"
+                android:src="@drawable/record" />
+    
+            <ImageButton android:id="@+id/playButton"
+                android:layout_height="fill_parent" 
+                style="@android:style/MediaButton"
+                android:src="@drawable/play" />
+    
+            <ImageButton android:id="@+id/stopButton"
+                android:layout_height="fill_parent" 
+                style="@android:style/MediaButton"
+                android:src="@drawable/stop" />
 
-       </LinearLayout>
+        </LinearLayout>
 
-       </LinearLayout>
+    </LinearLayout>
     
 </LinearLayout>
index d5397bf..59672f2 100644 (file)
             android:layout_height="wrap_content"
             android:layout_below="@+id/stateMessage2Layout"
             android:layout_centerHorizontal="true"
-            android:text="@string/storage_available"
             style="@android:style/TextAppearance.Small" />                
 
-           <ProgressBar android:id="@+id/stateProgressBar"
-               android:orientation="horizontal"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content" 
-               android:layout_above="@+id/timerView"
-               android:max="100"
-               android:progress="0"
-               android:layout_marginLeft="20dip"
-               android:layout_marginRight="20dip"
-               style="?android:attr/progressBarStyleHorizontal" />
+        <ProgressBar android:id="@+id/stateProgressBar"
+            android:orientation="horizontal"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content" 
+            android:layout_above="@+id/timerView"
+            android:max="100"
+            android:progress="0"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            style="?android:attr/progressBarStyleHorizontal" />
 
     </RelativeLayout>
     
         android:layout_height="wrap_content"
         android:layout_weight="1">
 
-       <RelativeLayout
-               android:layout_width="fill_parent"
-               android:layout_height="fill_parent">
-
-               <LinearLayout android:id="@+id/exitButtons"
-                   android:orientation="horizontal"
-                   android:layout_width="fill_parent"
-                   android:layout_height="wrap_content" 
-                   android:layout_centerInParent="true" >
-                   
-                   <Button android:id="@+id/acceptButton"
-                       android:layout_width="fill_parent"
-                       android:layout_height="wrap_content"
-                       android:text="@string/accept"
-                       android:layout_weight="1"
-                           android:layout_marginLeft="15dip" 
-                           android:layout_marginRight="2dip" />
-           
-                   <Button android:id="@+id/discardButton"
-                       android:layout_width="fill_parent"
-                       android:layout_height="wrap_content"
-                       android:text="@string/discard"
-                       android:layout_weight="1"
-                       android:layout_marginLeft="2dip" 
-                       android:layout_marginRight="15dip" />
-               
-               </LinearLayout>
-               </RelativeLayout>
+            <LinearLayout android:id="@+id/exitButtons"
+                android:orientation="horizontal"
+                android:gravity="center_vertical"
+                android:layout_gravity="center_vertical"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content" 
+                android:layout_centerInParent="true" >
+                
+                <Button android:id="@+id/acceptButton"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/accept"
+                    android:layout_weight="1"
+                    android:layout_marginLeft="15dip" 
+                    android:layout_marginRight="2dip" />
+        
+                <Button android:id="@+id/discardButton"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/discard"
+                    android:layout_weight="1"
+                    android:layout_marginLeft="2dip" 
+                    android:layout_marginRight="15dip" />
+            
+            </LinearLayout>
     
-       <RelativeLayout
-               android:layout_width="fill_parent"
-               android:layout_height="fill_parent">
-               
-               <com.android.soundrecorder.VUMeter android:id="@+id/uvMeter"
-                   android:layout_width="200px"
-                   android:layout_height="80px"
-                   android:layout_centerInParent="true" />
-               </RelativeLayout>
+        <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent">
+            
+            <com.android.soundrecorder.VUMeter android:id="@+id/uvMeter"
+                android:layout_width="200px"
+                android:layout_height="80px"
+                android:layout_centerInParent="true" />
+        </RelativeLayout>
 
     </FrameLayout>
     
         android:gravity="center_horizontal"
         android:orientation="vertical">
 
-           <LinearLayout
-               android:layout_marginTop="4dip"
-               android:layout_marginBottom="4dip"
-           android:orientation="horizontal"
-               android:layout_width="wrap_content"
-               android:layout_height="fill_parent" >
+        <LinearLayout
+            android:layout_marginTop="4dip"
+            android:layout_marginBottom="4dip"
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent" >
 
-               <ImageButton android:id="@+id/recordButton"
-                       android:layout_height="fill_parent" 
-                               style="@android:style/MediaButton"
-                   android:src="@drawable/record" />
-       
-               <ImageButton android:id="@+id/playButton"
-                       android:layout_height="fill_parent" 
-                               style="@android:style/MediaButton"
-                   android:src="@drawable/play" />
-       
-               <ImageButton android:id="@+id/stopButton"
-                       android:layout_height="fill_parent" 
-                               style="@android:style/MediaButton"
-                   android:src="@drawable/stop" />
+            <ImageButton android:id="@+id/recordButton"
+                android:layout_height="fill_parent" 
+                style="@android:style/MediaButton"
+                android:src="@drawable/record" />
+    
+            <ImageButton android:id="@+id/playButton"
+                android:layout_height="fill_parent" 
+                style="@android:style/MediaButton"
+                android:src="@drawable/play" />
+    
+            <ImageButton android:id="@+id/stopButton"
+                android:layout_height="fill_parent" 
+                style="@android:style/MediaButton"
+                android:src="@drawable/stop" />
 
-       </LinearLayout>
+        </LinearLayout>
 
-       </LinearLayout>
+    </LinearLayout>
     
 </LinearLayout>
diff --git a/res/values-de-rDE/strings.xml b/res/values-de-rDE/strings.xml
deleted file mode 100644 (file)
index 567e493..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="accept">Diese Aufnahme verwenden</string>
-  <string name="app_name">Sound-Recorder</string>
-  <string name="audio_db_album_name">Audioaufnahmen</string>
-  <string name="audio_db_artist_name">Eigene Aufnahmen</string>
-  <string name="audio_db_playlist_name">Eigene Aufnahmen</string>
-  <string name="audio_db_title_format">
-                                       <xliff:g id="format">TT-MM-JJJJ SS:mm:ss</xliff:g>
-                               </string>
-  <string name="button_ok">OK</string>
-  <string name="discard">Verwerfen</string>
-  <string name="error_app_internal">Interner Anwendungsfehler</string>
-  <string name="error_mediadb_new_record">Audioaufnahme kann nicht gespeichert werden</string>
-  <string name="error_sdcard_access">Zugriff auf SD-Karte nicht möglich</string>
-  <string name="insert_sd_card">Setzen Sie SD-Karte ein</string>
-  <string name="message_recorded">Aufgenommene Nachricht</string>
-  <string name="min_available"><xliff:g id="minutes">%d</xliff:g> Min. verfügbar</string>
-  <string name="press_record">Aufnahmefunktion drücken</string>
-  <string name="record_your_message">Nachricht aufnehmen</string>
-  <string name="recording">Nimmt auf</string>
-  <string name="recording_stopped">Aufnehmen angehalten</string>
-  <string name="review_message">Nachricht prüfen</string>
-  <string name="sec_available"><xliff:g id="seconds">%d</xliff:g> verfügbar</string>
-  <string name="storage_available"><xliff:g id="xxx">%02d:%02d</xliff:g> verfügbar</string>
-  <string name="storage_is_full">Speicher ist voll</string>
-  <string name="timer_format">
-                                       <xliff:g id="format">%02d:%02d</xliff:g>
-                               </string>
-</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
new file mode 100644 (file)
index 0000000..8ceecdb
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">"Soundrekorder"</string>
+    <string name="record_your_message">"Ihre Nachricht aufnehmen"</string>
+    <string name="message_recorded">"Nachricht aufgezeichnet"</string>
+    <string name="review_message">"Nachricht erneut anhören"</string>
+    <string name="recording">"Aufnahme"</string>
+    <string name="recording_stopped">"Aufnahme beendet"</string>
+    <string name="storage_is_full">"Speicher ist voll"</string>
+    <!-- no translation found for max_length_reached (6398215743584093353) -->
+    <skip />
+    <string name="insert_sd_card">"SD-Karte einlegen"</string>
+    <string name="min_available">"<xliff:g id="MINUTES">%d</xliff:g> Min. verfügbar"</string>
+    <string name="sec_available">"<xliff:g id="SECONDS">%d</xliff:g> Sek. verfügbar"</string>
+    <string name="accept">"Diese Aufnahme verwenden"</string>
+    <string name="discard">"Verwerfen"</string>
+    <string name="button_ok">"OK"</string>
+    <string name="press_record">"Aufnahmetaste drücken"</string>
+    <!-- no translation found for audio_db_title_format (7912182366970749312) -->
+    <skip />
+    <string name="audio_db_artist_name">"Ihre Aufnahmen"</string>
+    <string name="audio_db_album_name">"Audioaufnahmen"</string>
+    <string name="audio_db_playlist_name">"Meine Aufnahmen"</string>
+    <string name="error_sdcard_access">"Kein Zugriff auf SD-Karte möglich."</string>
+    <string name="error_app_internal">"Interner Anwendungsfehler"</string>
+    <string name="error_mediadb_new_record">"Aufgenommenen Audioinhalt speichern"</string>
+</resources>
index 107378f..3c5d1d9 100644 (file)
@@ -1,26 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="accept">Use this recording</string>
-  <string name="app_name">SoundRecorder</string>
-  <string name="audio_db_album_name">Audio recordings</string>
-  <string name="audio_db_artist_name">Your recordings</string>
-  <string name="audio_db_title_format"><xliff:g id="format">dd-MM-yyyy HH:mm:ss</xliff:g></string>
-  <string name="button_ok">OK</string>
-  <string name="discard">Discard</string>
-  <string name="error_app_internal">Internal application error</string>
-  <string name="error_mediadb_new_record">Unable to save recorded audio</string>
-  <string name="error_sdcard_access">Unable to access SD card</string>
-  <string name="insert_sd_card">Please insert an SD card</string>
-  <string name="message_recorded">Message recorded</string>
-  <string name="min_available"><xliff:g id="minutes">%d</xliff:g> min available</string>
-  <string name="press_record">Press record</string>
-  <string name="record_your_message">Record your message</string>
-  <string name="recording">Recording</string>
-  <string name="recording_stopped">Recording stopped</string>
-  <string name="review_message">Review message</string>
-  <string name="sec_available"><xliff:g id="seconds">%d</xliff:g>s available</string>
-  <string name="storage_available"><xliff:g id="xxx">%02d:%02d</xliff:g> available</string>
-  <string name="storage_is_full">Storage is full</string>
-  <string name="timer_format"><xliff:g id="format">%02d:%02d</xliff:g></string>
+    <string name="app_name">"SoundRecorder"</string>
+    <string name="record_your_message">"Record your message"</string>
+    <string name="message_recorded">"Message recorded"</string>
+    <string name="review_message">"Review message"</string>
+    <string name="recording">"Recording"</string>
+    <string name="recording_stopped">"Recording stopped"</string>
+    <string name="storage_is_full">"Storage is full"</string>
+    <!-- no translation found for max_length_reached (8913888728170889194) -->
+    <skip />
+    <string name="insert_sd_card">"Please insert an SD card"</string>
+    <string name="min_available">"<xliff:g id="MINUTES">%d</xliff:g> min available"</string>
+    <string name="sec_available">"<xliff:g id="SECONDS">%d</xliff:g>s available"</string>
+    <string name="accept">"Use this recording"</string>
+    <string name="discard">"Discard"</string>
+    <string name="button_ok">"OK"</string>
+    <string name="press_record">"Press record"</string>
+    <string name="audio_db_title_format">"<xliff:g id="FORMAT">yyyy-MM-dd HH:mm:ss</xliff:g>"</string>
+    <string name="audio_db_artist_name">"Your recordings"</string>
+    <string name="audio_db_album_name">"Audio recordings"</string>
+    <!-- no translation found for audio_db_playlist_name (1430242423950663735) -->
+    <skip />
+    <string name="error_sdcard_access">"Unable to access SD card"</string>
+    <string name="error_app_internal">"Internal application error"</string>
+    <string name="error_mediadb_new_record">"Unable to save recorded audio"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
deleted file mode 100644 (file)
index 616bb47..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="accept">Utilizar esta grabación</string>
-  <string name="app_name">Grabadora de sonido</string>
-  <string name="audio_db_album_name">Grabaciones de audio</string>
-  <string name="audio_db_artist_name">Sus grabaciones</string>
-  <string name="audio_db_playlist_name">Mis grabaciones</string>
-  <string name="audio_db_title_format">
-                                       <xliff:g id="format">dd-MM-aaaa HH:mm:ss</xliff:g>
-                               </string>
-  <string name="button_ok">Aceptar</string>
-  <string name="discard">Rechazar</string>
-  <string name="error_app_internal">Error en aplicación interna</string>
-  <string name="error_mediadb_new_record">Incapaz de guardar audio grabado</string>
-  <string name="error_sdcard_access">Incapaz de acceder a tarjeta SD</string>
-  <string name="insert_sd_card">Inserte una tarjeta SD</string>
-  <string name="message_recorded">Mensaje grabado</string>
-  <string name="min_available"><xliff:g id="minutes">%d</xliff:g> min disponible</string>
-  <string name="press_record">Pulsar grabación</string>
-  <string name="record_your_message">Grabar su mensaje</string>
-  <string name="recording">Grabación</string>
-  <string name="recording_stopped">Grabación detenida</string>
-  <string name="review_message">Revisar mensaje</string>
-  <string name="sec_available"><xliff:g id="seconds">%d</xliff:g>s disponible</string>
-  <string name="storage_available"><xliff:g id="xxx">%02d:%02d</xliff:g> disponible</string>
-  <string name="storage_is_full">Almacenamiento lleno</string>
-  <string name="timer_format">
-                                       <xliff:g id="format">%02d:%02d</xliff:g>
-                               </string>
-</resources>
diff --git a/res/values-fr-rFR/strings.xml b/res/values-fr-rFR/strings.xml
deleted file mode 100644 (file)
index aed2580..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="accept">Utiliser cet enregistrement</string>
-  <string name="app_name">Magnétophone</string>
-  <string name="audio_db_album_name">Enregistrements audio</string>
-  <string name="audio_db_artist_name">Vos enregistrements</string>
-  <string name="audio_db_playlist_name">Mes enregistrements</string>
-  <string name="audio_db_title_format">
-                                       <xliff:g id="format">aaaa-MM-jj HH:mm:ss</xliff:g>
-                               </string>
-  <string name="button_ok">OK</string>
-  <string name="discard">Abandonner</string>
-  <string name="error_app_internal">Erreur d\'application interne</string>
-  <string name="error_mediadb_new_record">Impossible d\'enregistrer le fichier audio</string>
-  <string name="error_sdcard_access">Impossible à la carte la carte SD</string>
-  <string name="insert_sd_card">Veuillez insérer une carte SD</string>
-  <string name="message_recorded">Message enregistré</string>
-  <string name="min_available"><xliff:g id="minutes">%d</xliff:g> min disponible(s)</string>
-  <string name="press_record">Appuyez enregistrer</string>
-  <string name="record_your_message">Enregistrer votre message</string>
-  <string name="recording">Enregistrement</string>
-  <string name="recording_stopped">Enregistrement arrêté</string>
-  <string name="review_message">Aperçu du message</string>
-  <string name="sec_available"><xliff:g id="seconds">%d</xliff:g>s disponibles</string>
-  <string name="storage_available"><xliff:g id="xxx">%02d:%02d</xliff:g> disponible</string>
-  <string name="storage_is_full">Le stockage est plein</string>
-  <string name="timer_format">
-                                       <xliff:g id="format">%02d:%02d</xliff:g>
-                               </string>
-</resources>
diff --git a/res/values-it-rIT/strings.xml b/res/values-it-rIT/strings.xml
deleted file mode 100644 (file)
index 4f54254..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="accept">Utilizzare questa registrazione</string>
-  <string name="app_name">Registratore suoni</string>
-  <string name="audio_db_album_name">Registrazioni audio</string>
-  <string name="audio_db_artist_name">Registrazioni personali</string>
-  <string name="audio_db_playlist_name">Registrazioni</string>
-  <string name="audio_db_title_format">
-                                       <xliff:g id="format">gg-MM-aaaa HH:mm:ss</xliff:g>
-                               </string>
-  <string name="button_ok">OK</string>
-  <string name="discard">Ignora</string>
-  <string name="error_app_internal">Errore interno dell'applicazione</string>
-  <string name="error_mediadb_new_record">Impossibile salvare l'audio registrato</string>
-  <string name="error_sdcard_access">Impossibile accedere alla scheda SD</string>
-  <string name="insert_sd_card">Inserire una scheda SD</string>
-  <string name="message_recorded">Messaggio registrato</string>
-  <string name="min_available"><xliff:g id="minutes">%d</xliff:g> min disponibile</string>
-  <string name="press_record">Premere Registra</string>
-  <string name="record_your_message">Registrare il messaggio</string>
-  <string name="recording">Registrazione in corso</string>
-  <string name="recording_stopped">Registrazione interrotta</string>
-  <string name="review_message">Controlla messaggio</string>
-  <string name="sec_available"><xliff:g id="seconds">%d</xliff:g> disponibile</string>
-  <string name="storage_available"><xliff:g id="xxx">%02d:%02d</xliff:g> disponibile</string>
-  <string name="storage_is_full">La memoria è piena</string>
-  <string name="timer_format">
-                                       <xliff:g id="format">%02d:%02d</xliff:g>
-                               </string>
-</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
new file mode 100644 (file)
index 0000000..4871714
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">"音声レコーダー"</string>
+    <string name="record_your_message">"メッセージの録音"</string>
+    <string name="message_recorded">"メッセージを記録しました"</string>
+    <string name="review_message">"メッセージの確認"</string>
+    <string name="recording">"録音中"</string>
+    <string name="recording_stopped">"録音を停止しました"</string>
+    <string name="storage_is_full">"メモリがいっぱいです"</string>
+    <!-- no translation found for max_length_reached (6398215743584093353) -->
+    <skip />
+    <string name="insert_sd_card">"SDカードを挿入してください"</string>
+    <string name="min_available">"<xliff:g id="MINUTES">%d</xliff:g>分録音可能"</string>
+    <string name="sec_available">"<xliff:g id="SECONDS">%d</xliff:g>秒録音可能"</string>
+    <string name="accept">"この録音を使用"</string>
+    <string name="discard">"破棄"</string>
+    <string name="button_ok">"OK"</string>
+    <string name="press_record">"[録音]を押す"</string>
+    <!-- no translation found for audio_db_title_format (7912182366970749312) -->
+    <skip />
+    <string name="audio_db_artist_name">"録音リスト"</string>
+    <string name="audio_db_album_name">"録音"</string>
+    <string name="audio_db_playlist_name">"マイ録音"</string>
+    <string name="error_sdcard_access">"SDカードにアクセスできません"</string>
+    <string name="error_app_internal">"アプリケーションの内部エラー"</string>
+    <string name="error_mediadb_new_record">"録音を保存できません"</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
deleted file mode 100644 (file)
index f9d7fd0..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="accept">使用此錄音</string>
-  <string name="app_name">錄音機</string>
-  <string name="audio_db_album_name">音訊錄製</string>
-  <string name="audio_db_artist_name">您的錄音</string>
-  <string name="audio_db_playlist_name">我的錄音</string>
-  <string name="audio_db_title_format">
-                                       <xliff:g id="format">yyyy-MM-dd HH:mm:ss</xliff:g>
-                               </string>
-  <string name="button_ok">確定</string>
-  <string name="discard">放棄</string>
-  <string name="error_app_internal">應用程式內部錯誤</string>
-  <string name="error_mediadb_new_record">無法儲存錄製的音訊</string>
-  <string name="error_sdcard_access">無法存取 SD 卡</string>
-  <string name="insert_sd_card">請插入 SD 卡</string>
-  <string name="message_recorded">錄製的訊息</string>
-  <string name="min_available">剩餘 <xliff:g id="minutes">%d</xliff:g> 分可用</string>
-  <string name="press_record">按下錄製</string>
-  <string name="record_your_message">錄製您的訊息</string>
-  <string name="recording">正在錄製</string>
-  <string name="recording_stopped">已經停止錄製</string>
-  <string name="review_message">檢閱訊息</string>
-  <string name="sec_available"><xliff:g id="seconds">%d</xliff:g> 可用</string>
-  <string name="storage_available"><xliff:g id="xxx">%02d:%02d</xliff:g> 可用</string>
-  <string name="storage_is_full">儲存空間已滿</string>
-  <string name="timer_format">
-                                       <xliff:g id="format">%02d:%02d</xliff:g>
-                               </string>
-</resources>
index 0d96fed..1909ee1 100644 (file)
@@ -1,29 +1,53 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- application name and title of error dialogs-->
     <string name="app_name">Sound Recorder</string>
 
+    <!-- Screen title before and during recording -->
     <string name="record_your_message">Record your message</string>
+    <!-- Screen title after recording -->
     <string name="message_recorded">Message recorded</string>
+    <!-- Screen title while playing back a recording -->
     <string name="review_message">Review message</string>
+    <!-- Label shown during recording -->
     <string name="recording">Recording</string>
+    <!-- Label shown when the recording is stopped for a reason other than the user stopping it (e.g. the sd card was removed) -->
     <string name="recording_stopped">Recording stopped</string>
+    <!-- label shown when there is not enough space to record something -->
     <string name="storage_is_full">Storage is full</string>
+    <!-- label shown when the recording has reached maximum allowed file size -->
+    <string name="max_length_reached">Maximum length reached</string>
+    <!-- label shown when there is no sd card available to record to -->
     <string name="insert_sd_card">Please insert an SD card</string>
+    <!-- label shown when there is more than 1 minute but less than 9 minutes of space left to record -->
     <string name="min_available"><xliff:g id="minutes">%d</xliff:g> min available</string>
+    <!-- label shown when there is less than 1 minute of space left to record -->
     <string name="sec_available"><xliff:g id="seconds">%d</xliff:g>s available</string>
-    <string name="storage_available"><xliff:g id="xxx">%02d:%02d</xliff:g> available</string>
+
+    <!-- button to accept the current recording and return it to the caller -->
     <string name="accept">Use this recording</string>
+    <!-- button to discard the current recording and return to the caller -->
     <string name="discard">Discard</string>
+    <!-- acknowlegement button in a number of dialogs -->
     <string name="button_ok">OK</string>
+    <!-- Do not translate. Format of the timer that shows how much has been recorded so far -->
     <string name="timer_format"><xliff:g id="format">%02d:%02d</xliff:g></string>
+    <!-- label shown before the user has recorded anything -->
     <string name="press_record">Press record</string>
-    
+   
+    <!-- the name under which recordings will be visible in the media database is formatted like this --> 
     <string name="audio_db_title_format"><xliff:g id="format">yyyy-MM-dd HH:mm:ss</xliff:g></string>
+    <!-- all recordings will show up in the media database with this 'artist' name -->
     <string name="audio_db_artist_name">Your recordings</string>
+    <!-- all recordings will show up in the media database with this 'album' name -->
     <string name="audio_db_album_name">Audio recordings</string>
+    <!-- all recordings will show up in the media database in a playlist with this name -->
     <string name="audio_db_playlist_name">My recordings</string>
-    
+   
+    <!-- shown as the message in a dialog when an error occured because of an error accessing the sd card -->
     <string name="error_sdcard_access">Unable to access SD card</string>
+    <!-- shown as the message in a dialog when the app encountered an unspecified internal error -->
     <string name="error_app_internal">Internal application error</string>
+    <!-- shown as the message in a dialog when the recording could not be added to the media database -->
     <string name="error_mediadb_new_record">Unable to save recorded audio</string>
 </resources>
index 32fba51..5fe41b0 100644 (file)
@@ -13,7 +13,6 @@ import android.util.Log;
 
 public class Recorder implements OnCompletionListener, OnErrorListener {
     static final String SAMPLE_PREFIX = "recording";
-    static final String SAMPLE_EXTENSION = ".amr"; // this is a lie. See comment in com.google.android.mms.pdu.PduPersister
     static final String SAMPLE_PATH_KEY = "sample_path";
     static final String SAMPLE_LENGTH_KEY = "sample_length";
 
@@ -125,7 +124,7 @@ public class Recorder implements OnCompletionListener, OnErrorListener {
         signalStateChanged(IDLE_STATE);
     }
     
-    public void startRecording() {
+    public void startRecording(int outputfileformat, String extension) {
         stop();
         
         if (mSampleFile == null) {
@@ -134,8 +133,7 @@ public class Recorder implements OnCompletionListener, OnErrorListener {
                 sampleDir = new File("/sdcard/sdcard");
             
             try {
-                mSampleFile = File.createTempFile(SAMPLE_PREFIX, SAMPLE_EXTENSION,
-                                                    sampleDir);
+                mSampleFile = File.createTempFile(SAMPLE_PREFIX, extension, sampleDir);
             } catch (IOException e) {
                 setError(SDCARD_ACCESS_ERROR);
                 return;
@@ -144,10 +142,20 @@ public class Recorder implements OnCompletionListener, OnErrorListener {
         
         mRecorder = new MediaRecorder();
         mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
-        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+        mRecorder.setOutputFormat(outputfileformat);
         mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
         mRecorder.setOutputFile(mSampleFile.getAbsolutePath());
-        mRecorder.prepare();
+
+        // Handle IOException
+        try {
+            mRecorder.prepare();
+        } catch(IOException exception) {
+            setError(INTERNAL_ERROR);
+            mRecorder.reset();
+            mRecorder.release();
+            mRecorder = null;
+            return;
+        }
         mRecorder.start();
 
         mSampleStart = System.currentTimeMillis();
index a06815f..2338362 100644 (file)
@@ -9,14 +9,20 @@ import android.app.AlertDialog;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Intent;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.BroadcastReceiver;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.media.MediaRecorder;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.PowerManager;
 import android.os.StatFs;
+import android.os.PowerManager.WakeLock;
 import android.provider.MediaStore;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -28,86 +34,172 @@ import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
-/*
- * The file grows in jumps every five seconds or so. This class interpolates in between the jumps
- * so we get a smooth countdown.
+/**
+ * Calculates remaining recording time based on available disk space and
+ * optionally a maximum recording file size.
+ * 
+ * The reason why this is not trivial is that the file grows in blocks
+ * every few seconds or so, while we want a smooth countdown.
  */
-class DiskSpaceCalculator {
-    private File mFile = null;
-    private long mBytesPerSecond = -1;
-    private long mBytesRemaining;
+
+class RemainingTimeCalculator {
+    public static final int UNKNOWN_LIMIT = 0;
+    public static final int FILE_SIZE_LIMIT = 1;
+    public static final int DISK_SPACE_LIMIT = 2;
+    
+    // which of the two limits we will hit (or have fit) first
+    private int mCurrentLowerLimit = UNKNOWN_LIMIT;
+    
+    private File mSDCardDirectory;
     
-    private long mStartTime;
-    private long mStartBytesRemaining;
+     // State for tracking file size of recording.
+    private File mRecordingFile;
+    private long mMaxBytes;
     
-    private long mPollTime;
+    // Rate at which the file grows
+    private int mBytesPerSecond;
     
-    public DiskSpaceCalculator() {
-        mFile = Environment.getExternalStorageDirectory();
+    // time at which number of free blocks last changed
+    private long mBlocksChangedTime;
+    // number of available blocks at that time
+    private long mLastBlocks;
+    
+    // time at which the size of the file has last changed
+    private long mFileSizeChangedTime;
+    // size of the file at that time
+    private long mLastFileSize;
+    
+    public RemainingTimeCalculator() {
+        mSDCardDirectory = Environment.getExternalStorageDirectory();
     }    
     
+    /**
+     * If called, the calculator will return the minimum of two estimates:
+     * how long until we run out of disk space and how long until the file
+     * reaches the specified size.
+     * 
+     * @param file the file to watch
+     * @param maxBytes the limit
+     */
+    
+    public void setFileSizeLimit(File file, long maxBytes) {
+        mRecordingFile = file;
+        mMaxBytes = maxBytes;
+    }
+    
+    /**
+     * Resets the interpolation.
+     */
     public void reset() {
-        mBytesPerSecond = -1;
-        mBytesRemaining = -1;
-        mStartBytesRemaining = -1;
+        mCurrentLowerLimit = UNKNOWN_LIMIT;
+        mBlocksChangedTime = -1;
+        mFileSizeChangedTime = -1;
     }
     
-    /*
-     * Updates mBytesPerSecond, returns ammount of disk space available
+    /**
+     * Returns how long (in seconds) we can continue recording. 
      */
-    public long pollDiskSpace() {
-        StatFs fs = new StatFs(mFile.getAbsolutePath());
-        long numBlocks = fs.getAvailableBlocks();
+    public long timeRemaining() {
+        // Calculate how long we can record based on free disk space
+        
+        StatFs fs = new StatFs(mSDCardDirectory.getAbsolutePath());
+        long blocks = fs.getAvailableBlocks();
         long blockSize = fs.getBlockSize();
-        long b = numBlocks * blockSize;
-        long t = System.currentTimeMillis();
+        long now = System.currentTimeMillis();
         
-        if (b == mBytesRemaining)
-            return b; // nothing changed, don't recalculate mBytePerSecond
+        if (mBlocksChangedTime == -1 || blocks != mLastBlocks) {
+            mBlocksChangedTime = now;
+            mLastBlocks = blocks;
+        }
+
+        /* The calculation below always leaves one free block, since free space
+           in the block we're currently writing to is not added. This
+           last block might get nibbled when we close and flush the file, but 
+           we won't run out of disk. */
         
-        mPollTime = t;
-        mBytesRemaining = b;
+        // at mBlocksChangedTime we had this much time
+        long result = mLastBlocks*blockSize/mBytesPerSecond;
+        // so now we have this much time
+        result -= (now - mBlocksChangedTime)/1000;
         
-        if (mBytesRemaining > mStartBytesRemaining) {
-            // first call or space got freed up, reset the calculation    
-            mStartBytesRemaining = mBytesRemaining;
-            mStartTime = mPollTime;
-            return b;
+        if (mRecordingFile == null) {
+            mCurrentLowerLimit = DISK_SPACE_LIMIT;
+            return result;
+        }
+        
+        // If we have a recording file set, we calculate a second estimate
+        // based on how long it will take us to reach mMaxBytes.
+        
+        mRecordingFile = new File(mRecordingFile.getAbsolutePath());
+        long fileSize = mRecordingFile.length();
+        if (mFileSizeChangedTime == -1 || fileSize != mLastFileSize) {
+            mFileSizeChangedTime = now;
+            mLastFileSize = fileSize;
         }
 
-        long size = mStartBytesRemaining - mBytesRemaining;
-        long time = (mPollTime - mStartTime)/1000;
-        if (time > 0)
-            mBytesPerSecond = size/time;
+        long result2 = (mMaxBytes - fileSize)/mBytesPerSecond;
+        result2 -= (now - mFileSizeChangedTime)/1000;
+        result2 -= 1; // just for safety
         
-        return b;
+        mCurrentLowerLimit = result < result2
+            ? DISK_SPACE_LIMIT : FILE_SIZE_LIMIT;
+        
+        return Math.min(result, result2);
     }
     
-    public long timeRemaining() {
-        if (mBytesPerSecond <= 0)
-            return -1;
-    
-        long bytes = mBytesRemaining - mBytesPerSecond*(System.currentTimeMillis() - mPollTime)/1000;
-        return bytes/mBytesPerSecond;
+    /**
+     * Indicates which limit we will hit (or have hit) first, by returning one 
+     * of FILE_SIZE_LIMIT or DISK_SPACE_LIMIT or UNKNOWN_LIMIT. We need this to 
+     * display the correct message to the user when we hit one of the limits.
+     */
+    public int currentLowerLimit() {
+        return mCurrentLowerLimit;
+    }
+
+    /**
+     * Is there any point of trying to start recording?
+     */
+    public boolean diskSpaceAvailable() {
+        StatFs fs = new StatFs(mSDCardDirectory.getAbsolutePath());
+        // keep one free block
+        return fs.getAvailableBlocks() > 1;
+    }
+
+    /**
+     * Sets the bit rate used in the interpolation.
+     *
+     * @param bitRate the bit rate to set in bits/sec.
+     */
+    public void setBitRate(int bitRate) {
+        mBytesPerSecond = bitRate/8;
     }
-    
 }
 
 public class SoundRecorder extends Activity 
         implements Button.OnClickListener, Recorder.OnStateChangedListener {
     static final String TAG = "SoundRecorder";
     static final String STATE_FILE_NAME = "soundrecorder.state";
-    static final String MIME_TYPE = "audio/amr"; // this is a lie. See comment in com.google.android.mms.pdu.PduPersister
     static final String RECORDER_STATE_KEY = "recorder_state";
     static final String SAMPLE_INTERRUPTED_KEY = "sample_interrupted";
-   
+    static final String MAX_FILE_SIZE_KEY = "max_file_size";
+
+    static final String AUDIO_3GPP = "audio/3gpp";
+    static final String AUDIO_AMR = "audio/amr";
+    static final String AUDIO_ANY = "audio/*";
+    
+    static final int BITRATE_AMR =  5900; // bits/sec
+    static final int BITRATE_3GPP = 5900;
+    
+    WakeLock mWakeLock;
+    String mRequestedType = AUDIO_ANY;
     Recorder mRecorder;
     boolean mSampleInterrupted = false;    
     String mErrorUiMessage = null; // Some error messages are displayed in the UI, 
                                    // not a dialog. This happens when a recording
                                    // is interrupted for some reason.
     
-    DiskSpaceCalculator mDiskSpaceCalculator;
+    long mMaxFileSize = -1;        // can be specified in the intent
+    RemainingTimeCalculator mRemainingTimeCalculator;
     
     String mTimerFormat;
     final Handler mHandler = new Handler();
@@ -129,26 +221,54 @@ public class SoundRecorder extends Activity
     Button mAcceptButton;
     Button mDiscardButton;
     VUMeter mVUMeter;
+    private BroadcastReceiver mSDCardMountEventReceiver = null;
 
     @Override
     public void onCreate(Bundle icycle) {
         super.onCreate(icycle);
 
+        Intent i = getIntent();
+        if (i != null) {
+            String s = i.getType();
+            if (AUDIO_AMR.equals(s) || AUDIO_3GPP.equals(s) || AUDIO_ANY.equals(s)) {
+                mRequestedType = s;
+            } else if (s != null) {
+                // we only support amr and 3gpp formats right now 
+                setResult(RESULT_CANCELED);
+                finish();
+                return;
+            }
+            
+            final String EXTRA_MAX_BYTES
+                = android.provider.MediaStore.Audio.Media.EXTRA_MAX_BYTES;
+            mMaxFileSize = i.getLongExtra(EXTRA_MAX_BYTES, -1);
+        }
+        
+        if (AUDIO_ANY.equals(mRequestedType)) {
+            mRequestedType = AUDIO_3GPP;
+        }
+        
         setContentView(R.layout.main);
 
         mRecorder = new Recorder();
         mRecorder.setOnStateChangedListener(this);
-        mDiskSpaceCalculator = new DiskSpaceCalculator();
+        mRemainingTimeCalculator = new RemainingTimeCalculator();
+
+        PowerManager pm 
+            = (PowerManager) getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, 
+                                    "SoundRecorder");
 
         initResourceRefs();
         
         setResult(RESULT_CANCELED);
-
+        registerExternalStorageListener();
         if (icycle != null) {
             Bundle recorderState = icycle.getBundle(RECORDER_STATE_KEY);
             if (recorderState != null) {
                 mRecorder.restoreState(recorderState);
                 mSampleInterrupted = recorderState.getBoolean(SAMPLE_INTERRUPTED_KEY, false);
+                mMaxFileSize = recorderState.getLong(MAX_FILE_SIZE_KEY, -1);
             }
         }
         
@@ -175,10 +295,15 @@ public class SoundRecorder extends Activity
         
         mRecorder.saveState(recorderState);
         recorderState.putBoolean(SAMPLE_INTERRUPTED_KEY, mSampleInterrupted);
+        recorderState.putLong(MAX_FILE_SIZE_KEY, mMaxFileSize);
         
         outState.putBundle(RECORDER_STATE_KEY, recorderState);
     }
     
+    /*
+     * Whenever the UI is re-created (due f.ex. to orientation change) we have
+     * to reinitialize references to the views.
+     */
     private void initResourceRefs() {
         mRecordButton = (ImageButton) findViewById(R.id.recordButton);
         mPlayButton = (ImageButton) findViewById(R.id.playButton);
@@ -206,22 +331,39 @@ public class SoundRecorder extends Activity
         mVUMeter.setRecorder(mRecorder);
     }
     
+    /*
+     * Handle the buttons.
+     */
     public void onClick(View button) {
         if (!button.isEnabled())
             return;
 
         switch (button.getId()) {
             case R.id.recordButton:
+                mRemainingTimeCalculator.reset();
                 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                     mSampleInterrupted = true;
                     mErrorUiMessage = getResources().getString(R.string.insert_sd_card);
                     updateUi();
-                } else if (mDiskSpaceCalculator.pollDiskSpace() < 1024) {
+                } else if (!mRemainingTimeCalculator.diskSpaceAvailable()) {
                     mSampleInterrupted = true;
                     mErrorUiMessage = getResources().getString(R.string.storage_is_full);
                     updateUi();
                 } else {
-                    mRecorder.startRecording();
+                    if (AUDIO_AMR.equals(mRequestedType)) {
+                        mRemainingTimeCalculator.setBitRate(BITRATE_AMR);
+                        mRecorder.startRecording(MediaRecorder.OutputFormat.RAW_AMR, ".amr");
+                    } else if (AUDIO_3GPP.equals(mRequestedType)) {
+                        mRemainingTimeCalculator.setBitRate(BITRATE_3GPP);
+                        mRecorder.startRecording(MediaRecorder.OutputFormat.THREE_GPP, ".3gpp");
+                    } else {
+                        throw new IllegalArgumentException("Invalid output file type requested");
+                    }
+                    
+                    if (mMaxFileSize != -1) {
+                        mRemainingTimeCalculator.setFileSizeLimit(
+                                mRecorder.sampleFile(), mMaxFileSize);
+                    }
                 }
                 break;
             case R.id.playButton:
@@ -242,6 +384,9 @@ public class SoundRecorder extends Activity
         }
     }
     
+    /*
+     * Handle the "back" hardware key. 
+     */
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
@@ -279,16 +424,64 @@ public class SoundRecorder extends Activity
         super.onPause();
     }
 
+    /*
+     * If we have just recorded a smaple, this adds it to the media data base
+     * and sets the result to the sample's URI.
+     */
     private void saveSample() {
         if (mRecorder.sampleLength() == 0)
             return;
-        Uri uri = this.addToMediaDB(mRecorder.sampleFile());
-        if (uri == null)
+        Uri uri = null;
+        try {
+            uri = this.addToMediaDB(mRecorder.sampleFile());
+        } catch(UnsupportedOperationException ex) {  // Database manipulation failure
             return;
+        }
+        if (uri == null) {
+            return;
+        }
         setResult(RESULT_OK, new Intent().setData(uri));
     }
     
     /*
+     * Called on destroy to unregister the SD card mount event receiver.
+     */
+    @Override
+    public void onDestroy() {
+        if (mSDCardMountEventReceiver != null) {
+            unregisterReceiver(mSDCardMountEventReceiver);
+            mSDCardMountEventReceiver = null;
+        }
+        super.onDestroy();
+    }
+    
+    /*
+     * Registers an intent to listen for ACTION_MEDIA_EJECT/ACTION_MEDIA_MOUNTED
+     * notifications.
+     */
+    private void registerExternalStorageListener() {
+        if (mSDCardMountEventReceiver == null) {
+            mSDCardMountEventReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    String action = intent.getAction();
+                    if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
+                        mRecorder.delete();
+                    } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
+                        mSampleInterrupted = false;
+                        updateUi();
+                    }
+                }
+            };
+            IntentFilter iFilter = new IntentFilter();
+            iFilter.addAction(Intent.ACTION_MEDIA_EJECT);
+            iFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
+            iFilter.addDataScheme("file");
+            registerReceiver(mSDCardMountEventReceiver, iFilter);
+        }
+    }
+
+    /*
      * A simple utility to do a query into the databases.
      */
     private Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
@@ -384,8 +577,7 @@ public class SoundRecorder extends Activity
         cv.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());
         cv.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
         cv.put(MediaStore.Audio.Media.DATE_MODIFIED, (int) (modDate / 1000));
-//        cv.put(MediaStore.Audio.MediaColumns.DURATION, mRecordingLength);
-        cv.put(MediaStore.Audio.Media.MIME_TYPE, MIME_TYPE);
+        cv.put(MediaStore.Audio.Media.MIME_TYPE, mRequestedType);
         cv.put(MediaStore.Audio.Media.ARTIST,
                 res.getString(R.string.audio_db_artist_name));
         cv.put(MediaStore.Audio.Media.ALBUM,
@@ -417,7 +609,8 @@ public class SoundRecorder extends Activity
     }
 
     /**
-     * This is the big MM:SS timer.
+     * Update the big MM:SS timer. If we are in playback, also update the
+     * progress bar.
      */
     private void updateTimerView() {
         Resources res = getResources();
@@ -439,20 +632,32 @@ public class SoundRecorder extends Activity
             mHandler.postDelayed(mUpdateTimer, 1000);
     }
 
+    /*
+     * Called when we're in recording state. Find out how much longer we can 
+     * go on recording. If it's under 5 minutes, we display a count-down in 
+     * the UI. If we've run out of time, stop the recording. 
+     */
     private void updateTimeRemaining() {
-        mDiskSpaceCalculator.pollDiskSpace();
-        long t = mDiskSpaceCalculator.timeRemaining();
+        long t = mRemainingTimeCalculator.timeRemaining();
             
-        if (t == -1) {
-            mStateMessage1.setText("");
-            return;
-        } 
-        
-        t -= 5; // safety buffer of 5 secs
-
         if (t <= 0) {
             mSampleInterrupted = true;
-            mErrorUiMessage = getResources().getString(R.string.storage_is_full);
+
+            int limit = mRemainingTimeCalculator.currentLowerLimit();
+            switch (limit) {
+                case RemainingTimeCalculator.DISK_SPACE_LIMIT:
+                    mErrorUiMessage 
+                        = getResources().getString(R.string.storage_is_full);
+                    break;
+                case RemainingTimeCalculator.FILE_SIZE_LIMIT:
+                    mErrorUiMessage 
+                        = getResources().getString(R.string.max_length_reached);
+                    break;
+                default:
+                    mErrorUiMessage = null;
+                    break;
+            }
+            
             mRecorder.stop();
             return;
         }
@@ -579,18 +784,28 @@ public class SoundRecorder extends Activity
         mVUMeter.invalidate();
     }
     
+    /*
+     * Called when Recorder changed it's state.
+     */
     public void onStateChanged(int state) {
         if (state == Recorder.PLAYING_STATE || state == Recorder.RECORDING_STATE) {
             mSampleInterrupted = false;
             mErrorUiMessage = null;
         }
         
-        if (state == Recorder.RECORDING_STATE)
-            mDiskSpaceCalculator.reset();
+        if (state == Recorder.RECORDING_STATE) {
+            mWakeLock.acquire(); // we don't want to go to sleep while recording
+        } else {
+            if (mWakeLock.isHeld())
+                mWakeLock.release();
+        }
         
         updateUi();
     }
     
+    /*
+     * Called when MediaPlayer encounters an error.
+     */
     public void onError(int error) {
         Resources res = getResources();