Merge tag 'LA.UM.7.2.r2-04200-8x98.0' into int/p/fp2

Merge the CodeAurora version of the sound recorder. Amongst others, it
makes the app available in the launcher again, which we want for
consistency with previous Android releases on the FP2.

* tag 'LA.UM.7.2.r2-04200-8x98.0':
  SoundRecorder: avoid reading data from null array.
  SoundRecorder: support access hide APIs
  Revert "packages/apps/SoundRecorder: Set LOCAL_SDK_VERSION where possible."
  SoundRecorder: fix FC  when error.
  SoundRecorder: sync O changes to P.
  SoundRecorder: migration to android O

Issue: FP2P-124
Change-Id: I8e6d67948bbd25e4349fe2dcf96bc509392f1e80
diff --git a/Android.mk b/Android.mk
index 0154231..884daa8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,11 +1,40 @@
 LOCAL_PATH:= $(call my-dir)
+
+# make wrapper static lib
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src-Wrapper)
+LOCAL_MODULE := soundrecorder_wrapper
+LOCAL_MODULE_TAGS := optional
+
+# support access hidden APIs
+LOCAL_PRIVATE_PLATFORM_APIS:=true
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR += frameworks/support/v7/recyclerview/res
+
+LOCAL_AAPT_FLAGS += --auto-add-overlay
+LOCAL_AAPT_FLAGS += --extra-packages
+LOCAL_AAPT_FLAGS += android.support.v7.recyclerview
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
+LOCAL_STATIC_JAVA_LIBRARIES += soundrecorder_wrapper
+
 LOCAL_PACKAGE_NAME := SoundRecorder
-LOCAL_SDK_VERSION := current
+
+# support access hidden APIs
+LOCAL_PRIVATE_PLATFORM_APIS:=true
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PRIVILEGED_MODULE := true
 
 include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 11d9f6e..de81d93 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,25 +19,47 @@
 
     <original-package android:name="com.android.soundrecorder" />
 
+    <uses-sdk
+        android:minSdkVersion="21"/>
+
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
+
     <application android:label="@string/app_name"
                  android:icon="@drawable/ic_launcher_soundrecorder"
-                 android:usesCleartextTraffic="false">
+                 android:usesCleartextTraffic="false"
+                 android:supportsRtl="true">
         <activity android:name="SoundRecorder"
                 android:configChanges="orientation|screenSize|keyboardHidden"
                 android:screenOrientation="unspecified"
-                android:clearTaskOnLaunch="true"
                 android:theme="@style/Theme.SoundRecorder">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <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/amr" />
+                 <data android:mimeType="audio/3gpp" />
             </intent-filter>
             <intent-filter>
                  <action android:name="android.provider.MediaStore.RECORD_SOUND" />
                  <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".filelist.FileListActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:label="@string/file_list_activity_label"
+            android:theme="@style/FileListActivity">
+        </activity>
     </application>
 </manifest>
diff --git a/proguard.flags b/proguard.flags
new file mode 100644
index 0000000..eac5d6e
--- /dev/null
+++ b/proguard.flags
@@ -0,0 +1,2 @@
+#Avoid the library class dependency error
+-dontwarn android.support.v7.recyclerview.*
\ No newline at end of file
diff --git a/res/drawable-hdpi/ic_launcher_soundrecorder.png b/res/drawable-hdpi/ic_launcher_soundrecorder.png
index 053bd7a..f433f2b 100644
--- a/res/drawable-hdpi/ic_launcher_soundrecorder.png
+++ b/res/drawable-hdpi/ic_launcher_soundrecorder.png
Binary files differ
diff --git a/res/drawable-hdpi/record.png b/res/drawable-hdpi/record.png
deleted file mode 100644
index 8f81f0c..0000000
--- a/res/drawable-hdpi/record.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/record.png b/res/drawable-mdpi/record.png
deleted file mode 100644
index ae518d5..0000000
--- a/res/drawable-mdpi/record.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_soundrecorder.png b/res/drawable-xhdpi/ic_launcher_soundrecorder.png
new file mode 100644
index 0000000..dd980ae
--- /dev/null
+++ b/res/drawable-xhdpi/ic_launcher_soundrecorder.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_soundrecorder.png b/res/drawable-xxhdpi/ic_launcher_soundrecorder.png
new file mode 100644
index 0000000..8a212de
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher_soundrecorder.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_launcher_soundrecorder.png b/res/drawable-xxxhdpi/ic_launcher_soundrecorder.png
new file mode 100644
index 0000000..a06522a
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_launcher_soundrecorder.png
Binary files differ
diff --git a/res/drawable/action_bar_back.xml b/res/drawable/action_bar_back.xml
new file mode 100644
index 0000000..838c7f9
--- /dev/null
+++ b/res/drawable/action_bar_back.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M20,13L7.8,13L13.4,18.6L12,20L4,12L12,4L13.4,5.4L7.8,11L20,11L20,13L20,13Z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.87"
+        android:fillColor="#FFFFFF"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/action_button_background.xml b/res/drawable/action_button_background.xml
new file mode 100644
index 0000000..3f81407
--- /dev/null
+++ b/res/drawable/action_button_background.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/button_tint_color">
+    <item>
+        <shape android:shape="oval">
+            <solid android:color="@color/main_background"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/action_mode_back.xml b/res/drawable/action_mode_back.xml
new file mode 100644
index 0000000..15a82db
--- /dev/null
+++ b/res/drawable/action_mode_back.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M20,13L7.8,13L13.4,18.6L12,20L4,12L12,4L13.4,5.4L7.8,11L20,11L20,13L20,13Z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.87"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/action_mode_delete.xml b/res/drawable/action_mode_delete.xml
new file mode 100644
index 0000000..45c9a16
--- /dev/null
+++ b/res/drawable/action_mode_delete.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M6,19C6,20.1 6.9,21 8,21L16,21C17.1,21 18,20.1 18,19L18,7L6,7L6,19L6,19ZM19,4L15.5,4L14.5,3L9.5,3L8.5,4L5,4L5,6L19,6L19,4L19,4Z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.87"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/action_mode_drop_down.xml b/res/drawable/action_mode_drop_down.xml
new file mode 100644
index 0000000..17f8f12
--- /dev/null
+++ b/res/drawable/action_mode_drop_down.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M7,9L12.295,14.358L17.59,9L7,9Z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.87"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/action_mode_edit.xml b/res/drawable/action_mode_edit.xml
new file mode 100644
index 0000000..240e9e3
--- /dev/null
+++ b/res/drawable/action_mode_edit.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M3,17.2L3,21L6.8,21L17.8,9.9L14,6.1L3,17.2L3,17.2ZM20.7,7C21.1,6.6 21.1,6 20.7,5.6L18.4,3.3C18,2.9 17.4,2.9 17,3.3L15.2,5.1L19,8.9L20.7,7L20.7,7Z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.87"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/action_mode_share.xml b/res/drawable/action_mode_share.xml
new file mode 100644
index 0000000..e3605cf
--- /dev/null
+++ b/res/drawable/action_mode_share.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M8.41,10.02L14.8,6.33C14.71,6.15 14.71,5.88 14.71,5.7C14.71,4.17 15.88,3 17.41,3C18.94,3 20.11,4.17 20.11,5.7C20.11,7.23 18.94,8.4 17.41,8.4C16.69,8.4 16.06,8.13 15.61,7.68L9.22,11.37C9.31,11.55 9.31,11.82 9.31,12C9.31,12.18 9.31,12.45 9.22,12.63L15.61,16.41C16.06,15.96 16.69,15.69 17.41,15.69C18.85,15.69 20.02,16.86 20.02,18.3C20.02,19.74 18.85,20.91 17.41,20.91C15.97,20.91 14.8,19.74 14.8,18.3C14.8,18.12 14.8,17.94 14.89,17.67L8.5,13.89C8.05,14.34 7.42,14.61 6.7,14.61C5.17,14.61 4,13.44 4,11.91C4,10.38 5.08,9.3 6.61,9.3C7.33,9.3 7.96,9.57 8.41,10.02Z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.87"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/dialog_warning.xml b/res/drawable/dialog_warning.xml
new file mode 100644
index 0000000..6368adf
--- /dev/null
+++ b/res/drawable/dialog_warning.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="18dp"
+    android:height="18dp"
+    android:viewportHeight="18.0"
+    android:viewportWidth="18.0">
+    <path
+        android:fillColor="#EA2928"
+        android:pathData="M0,17L18,17L9,2L0,17L0,17ZM9.818,14.632L8.182,14.632L8.182,13.053L9.818,13.053L9.818,14.632L9.818,14.632ZM9.818,11.474L8.182,11.474L8.182,8.316L9.818,8.316L9.818,11.474L9.818,11.474Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
diff --git a/res/drawable/file_list_folder.xml b/res/drawable/file_list_folder.xml
new file mode 100644
index 0000000..ceb9534
--- /dev/null
+++ b/res/drawable/file_list_folder.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="26dp"
+    android:viewportHeight="28.0"
+    android:viewportWidth="34.0">
+    <path
+        android:fillAlpha="0.54"
+        android:fillColor="#000000"
+        android:pathData="M13.6,0L3.4,0C1.53,0 0.017,1.53 0.017,3.4L0,23.8C0,25.67 1.53,27.2 3.4,27.2L30.6,27.2C32.47,27.2 34,25.67 34,23.8L34,6.8C34,4.93 32.47,3.4 30.6,3.4L17,3.4L13.6,0L13.6,0Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
diff --git a/res/drawable/media_button_background.xml b/res/drawable/media_button_background.xml
new file mode 100644
index 0000000..3f81407
--- /dev/null
+++ b/res/drawable/media_button_background.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/button_tint_color">
+    <item>
+        <shape android:shape="oval">
+            <solid android:color="@color/main_background"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/media_list.xml b/res/drawable/media_list.xml
new file mode 100644
index 0000000..03f42f5
--- /dev/null
+++ b/res/drawable/media_list.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true" android:drawable="@drawable/media_list_normal"/>
+    <item android:state_enabled="false" android:drawable="@drawable/media_list_disable"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/media_list_disable.xml b/res/drawable/media_list_disable.xml
new file mode 100644
index 0000000..51a556f
--- /dev/null
+++ b/res/drawable/media_list_disable.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M8.333,13.333L21.333,13.333L21.333,10.667L8.333,10.667L8.333,13.333L8.333,13.333ZM8.333,4L8.333,6.667L21.333,6.667L21.333,4L8.333,4L8.333,4ZM8.333,20L21.333,20L21.333,17.333L8.333,17.333L8.333,20L8.333,20ZM3,13.333L5.667,13.333L5.667,10.667L3,10.667L3,13.333L3,13.333ZM3,4L3,6.667L5.667,6.667L5.667,4L3,4L3,4ZM3,20L5.667,20L5.667,17.333L3,17.333L3,20L3,20Z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.15"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/media_list_normal.xml b/res/drawable/media_list_normal.xml
new file mode 100644
index 0000000..1024743
--- /dev/null
+++ b/res/drawable/media_list_normal.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M8.333,13.333L21.333,13.333L21.333,10.667L8.333,10.667L8.333,13.333L8.333,13.333ZM8.333,4L8.333,6.667L21.333,6.667L21.333,4L8.333,4L8.333,4ZM8.333,20L21.333,20L21.333,17.333L8.333,17.333L8.333,20L8.333,20ZM3,13.333L5.667,13.333L5.667,10.667L3,10.667L3,13.333L3,13.333ZM3,4L3,6.667L5.667,6.667L5.667,4L3,4L3,4ZM3,20L5.667,20L5.667,17.333L3,17.333L3,20L3,20Z"
+        android:strokeWidth="1"
+        android:fillColor="#757575"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/media_play.xml b/res/drawable/media_play.xml
new file mode 100644
index 0000000..2c09488
--- /dev/null
+++ b/res/drawable/media_play.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="32dp"
+    android:viewportHeight="40.0"
+    android:viewportWidth="40.0">
+    <path
+        android:fillColor="#EF5350"
+        android:pathData="M20,0C8.96,0 0,8.96 0,20C0,31.04 8.96,40 20,40C31.04,40 40,31.04 40,20C40,8.96 31.04,0 20,0L20,0ZM15.95,29L15.95,11L27.95,20L15.95,29L15.95,29Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
diff --git a/res/drawable/media_stop.xml b/res/drawable/media_stop.xml
new file mode 100644
index 0000000..8157d6a
--- /dev/null
+++ b/res/drawable/media_stop.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true" android:drawable="@drawable/media_stop_disable"/>
+    <item android:state_enabled="false" android:drawable="@drawable/media_stop_normal"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/media_stop_disable.xml b/res/drawable/media_stop_disable.xml
new file mode 100644
index 0000000..341a215
--- /dev/null
+++ b/res/drawable/media_stop_disable.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M6,4L18,4A2,2 0,0 1,20 6L20,18A2,2 0,0 1,18 20L6,20A2,2 0,0 1,4 18L4,6A2,2 0,0 1,6 4z"
+        android:strokeWidth="1"
+        android:fillAlpha="0.54"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/media_stop_normal.xml b/res/drawable/media_stop_normal.xml
new file mode 100644
index 0000000..23fa823
--- /dev/null
+++ b/res/drawable/media_stop_normal.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M6,4L18,4A2,2 0,0 1,20 6L20,18A2,2 0,0 1,18 20L6,20A2,2 0,0 1,4 18L4,6A2,2 0,0 1,6 4z"
+        android:strokeWidth="1"
+        android:fillColor="#D9D9D9"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/pause.xml b/res/drawable/pause.xml
new file mode 100644
index 0000000..b2e729b
--- /dev/null
+++ b/res/drawable/pause.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="18.0"
+    android:viewportWidth="18.0">
+    <path
+        android:fillColor="#757575"
+        android:pathData="M2,1L6.571,1L6.571,17L2,17L2,1ZM11.143,1L15.714,1L15.714,17L11.143,17L11.143,1Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
diff --git a/res/drawable/player_panel_pause.xml b/res/drawable/player_panel_pause.xml
new file mode 100644
index 0000000..11ed5eb
--- /dev/null
+++ b/res/drawable/player_panel_pause.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M5,21.667L9.667,21.667L9.667,3L5,3L5,21.667L5,21.667ZM14.333,21.667L19,21.667L19,3L14.333,3L14.333,21.667L14.333,21.667Z"
+        android:strokeWidth="1"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/player_panel_play.xml b/res/drawable/player_panel_play.xml
new file mode 100644
index 0000000..0508bb5
--- /dev/null
+++ b/res/drawable/player_panel_play.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M7,21L7,3L19,12L7,21L7,21Z"
+        android:strokeWidth="1"
+        android:fillColor="#000000"
+        android:strokeColor="#00000000"/>
+</vector>
diff --git a/res/drawable/record.xml b/res/drawable/record.xml
new file mode 100644
index 0000000..8e55450
--- /dev/null
+++ b/res/drawable/record.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="18.0"
+    android:viewportWidth="18.0">
+    <path
+        android:fillColor="#E53935"
+        android:pathData="M9,9m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
diff --git a/res/layout-land/main.xml b/res/layout-land/main.xml
deleted file mode 100644
index e76aa2a..0000000
--- a/res/layout-land/main.xml
+++ /dev/null
@@ -1,174 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-    
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-    
-    <RelativeLayout android:id="@+id/timerViewLayout"
-        android:layout_width="match_parent"
-        android:layout_height="134dip"
-        android:background="@drawable/gradient_bg">
-
-        <TextView android:id="@+id/timerView"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textSize="115dip"
-            android:layout_alignParentRight="true"
-            android:layout_marginTop="-4dip"
-            android:layout_marginRight="10dip"
-            style="@android:style/TextAppearance.Large" />
-
-        <LinearLayout android:id="@+id/stateMessage2Layout"
-            android:orientation="horizontal"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentLeft="true"
-            android:baselineAligned="false"
-            android:layout_marginLeft="10dip"
-            android:layout_marginTop="20dip">
-            
-            <ImageView android:id="@+id/stateLED"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical" />
-                
-            <TextView android:id="@+id/stateMessage2"
-                android:layout_width="150dip"
-                android:layout_height="wrap_content"
-                android:text="@string/recording"
-                android:layout_gravity="center_vertical" 
-                android:layout_marginLeft="5px"
-                style="@android:style/TextAppearance.Medium"/>
-
-        </LinearLayout>
-    
-        <TextView android:id="@+id/stateMessage1"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@+id/stateMessage2Layout"
-            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"
-            android:layout_alignParentLeft="true"
-            android:layout_marginLeft="10dip"
-            android:layout_marginTop="20dip"
-            style="?android:attr/progressBarStyleHorizontal" />
-
-    </RelativeLayout>
-    
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1">
-
-            <LinearLayout android:id="@+id/exitButtons"
-                android:orientation="horizontal"
-                android:gravity="center_vertical"
-                android:layout_gravity="center_vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" 
-                android:layout_centerInParent="true" >
-
-                <Button android:id="@+id/discardButton"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:text="@string/discard"
-                    android:layout_weight="1"
-                    android:layout_marginLeft="2dip" 
-                    android:layout_marginRight="50dip" />
-
-                <Button android:id="@+id/acceptButton"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:text="@string/accept"
-                    android:layout_weight="1"
-                    android:layout_marginRight="2dip"
-                    android:layout_marginLeft="50dip" />
-            </LinearLayout>
-    
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-            
-            <com.android.soundrecorder.VUMeter android:id="@+id/uvMeter"
-                android:layout_width="150px"
-                android:layout_height="60px"
-                android:layout_centerInParent="true" />
-        </RelativeLayout>
-
-    </FrameLayout>
-    
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-
-                    
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="60dip"
-        android:background="@android:drawable/title_bar"
-        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="match_parent" >
-
-            <ImageButton android:id="@+id/recordButton"
-                android:layout_height="match_parent" 
-                style="@style/MediaButton"
-                android:src="@drawable/record" />
-    
-            <ImageButton android:id="@+id/playButton"
-                android:layout_height="match_parent" 
-                style="@style/MediaButton"
-                android:src="@drawable/play" />
-    
-            <ImageButton android:id="@+id/stopButton"
-                android:layout_height="match_parent" 
-                style="@style/MediaButton"
-                android:src="@drawable/stop" />
-
-        </LinearLayout>
-
-    </LinearLayout>
-    
-</LinearLayout>
diff --git a/res/layout-port/main.xml b/res/layout-port/main.xml
deleted file mode 100644
index 8b41ba3..0000000
--- a/res/layout-port/main.xml
+++ /dev/null
@@ -1,171 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-    
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-    
-    <RelativeLayout android:id="@+id/timerViewLayout"
-        android:layout_width="match_parent"
-        android:layout_height="242dip"
-        android:background="@drawable/gradient_bg">
-
-        <TextView android:id="@+id/timerView"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textSize="112dip"
-            android:layout_centerInParent="true"
-            style="@android:style/TextAppearance.Large" />
-
-        <LinearLayout android:id="@+id/stateMessage2Layout"
-            android:orientation="horizontal"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_above="@+id/timerView"
-            android:layout_centerHorizontal="true"
-            android:baselineAligned="false">
-            
-            <ImageView android:id="@+id/stateLED"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical" />
-                
-            <TextView android:id="@+id/stateMessage2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/recording"
-                android:layout_gravity="center_vertical" 
-                android:layout_marginLeft="5px"
-                style="@android:style/TextAppearance.Medium"/>
-
-        </LinearLayout>
-    
-        <TextView android:id="@+id/stateMessage1"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@+id/stateMessage2Layout"
-            android:layout_centerHorizontal="true"
-            style="@android:style/TextAppearance.Small" />                
-
-        <ProgressBar android:id="@+id/stateProgressBar"
-            android:orientation="horizontal"
-            android:layout_width="match_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>
-    
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1">
-
-            <LinearLayout android:id="@+id/exitButtons"
-                android:orientation="horizontal"
-                android:gravity="center_vertical"
-                android:layout_gravity="center_vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" 
-                android:layout_centerInParent="true" >
-
-                <Button android:id="@+id/discardButton"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:text="@string/discard"
-                    android:layout_weight="1"
-                    android:layout_marginLeft="2dip" 
-                    android:layout_marginRight="15dip" />
-
-                <Button android:id="@+id/acceptButton"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:text="@string/accept"
-                    android:layout_weight="1"
-                    android:layout_marginLeft="15dip"
-                    android:layout_marginRight="2dip" />
-            </LinearLayout>
-    
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-            
-            <com.android.soundrecorder.VUMeter android:id="@+id/uvMeter"
-                android:layout_width="200px"
-                android:layout_height="80px"
-                android:layout_centerInParent="true" />
-        </RelativeLayout>
-
-    </FrameLayout>
-    
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-
-                    
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="60dip"
-        android:background="@android:drawable/title_bar"
-        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="match_parent" >
-
-            <ImageButton android:id="@+id/recordButton"
-                android:layout_height="match_parent" 
-                style="@style/MediaButton"
-                android:src="@drawable/record" />
-    
-            <ImageButton android:id="@+id/playButton"
-                android:layout_height="match_parent" 
-                style="@style/MediaButton"
-                android:src="@drawable/play" />
-    
-            <ImageButton android:id="@+id/stopButton"
-                android:layout_height="match_parent" 
-                style="@style/MediaButton"
-                android:src="@drawable/stop" />
-
-        </LinearLayout>
-
-    </LinearLayout>
-    
-</LinearLayout>
diff --git a/res/layout-xlarge/main.xml b/res/layout-xlarge/main.xml
deleted file mode 100644
index 48cae46..0000000
--- a/res/layout-xlarge/main.xml
+++ /dev/null
@@ -1,171 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-
-    <RelativeLayout android:id="@+id/timerViewLayout"
-        android:layout_width="match_parent"
-        android:layout_height="242dip"
-        android:background="@drawable/gradient_bg">
-
-        <TextView android:id="@+id/timerView"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textSize="112dip"
-            android:layout_centerInParent="true"
-            style="@android:style/TextAppearance.Large" />
-
-        <LinearLayout android:id="@+id/stateMessage2Layout"
-            android:orientation="horizontal"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_above="@+id/timerView"
-            android:layout_centerHorizontal="true"
-            android:baselineAligned="false">
-
-            <ImageView android:id="@+id/stateLED"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical" />
-
-            <TextView android:id="@+id/stateMessage2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/recording"
-                android:layout_gravity="center_vertical"
-                android:layout_marginLeft="5px"
-                style="@android:style/TextAppearance.Medium"/>
-
-        </LinearLayout>
-
-        <TextView android:id="@+id/stateMessage1"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@+id/stateMessage2Layout"
-            android:layout_centerHorizontal="true"
-            style="@android:style/TextAppearance.Small" />
-
-        <ProgressBar android:id="@+id/stateProgressBar"
-            android:orientation="horizontal"
-            android:layout_width="match_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>
-
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1">
-
-            <LinearLayout android:id="@+id/exitButtons"
-                android:orientation="horizontal"
-                android:gravity="center_vertical"
-                android:layout_gravity="center_vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_centerInParent="true" >
-
-                <Button android:id="@+id/discardButton"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:text="@string/discard"
-                    android:layout_weight="1"
-                    android:layout_marginLeft="2dip"
-                    android:layout_marginRight="15dip" />
-
-                <Button android:id="@+id/acceptButton"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:text="@string/accept"
-                    android:layout_weight="1"
-                    android:layout_marginLeft="15dip"
-                    android:layout_marginRight="2dip" />
-            </LinearLayout>
-
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <com.android.soundrecorder.VUMeter android:id="@+id/uvMeter"
-                android:layout_width="200px"
-                android:layout_height="80px"
-                android:layout_centerInParent="true" />
-        </RelativeLayout>
-
-    </FrameLayout>
-
-    <ImageView
-         android:src="@android:drawable/divider_horizontal_dark"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:scaleType="fitXY"/>
-
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="60dip"
-        android:background="@android:drawable/title_bar"
-        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="match_parent" >
-
-            <ImageButton android:id="@+id/recordButton"
-                android:layout_height="match_parent"
-                style="@style/MediaButton"
-                android:src="@drawable/record" />
-
-            <ImageButton android:id="@+id/playButton"
-                android:layout_height="match_parent"
-                style="@style/MediaButton"
-                android:src="@drawable/play" />
-
-            <ImageButton android:id="@+id/stopButton"
-                android:layout_height="match_parent"
-                style="@style/MediaButton"
-                android:src="@drawable/stop" />
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/file_list_action_mode.xml b/res/layout/file_list_action_mode.xml
new file mode 100644
index 0000000..f7098ff
--- /dev/null
+++ b/res/layout/file_list_action_mode.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal">
+
+    <com.android.soundrecorder.actionmode.ButtonWithPopupMenu
+        android:id="@+id/selection_button"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:divider="?android:attr/listDividerAlertDialog"
+        android:gravity="start|center_vertical"
+        android:singleLine="true"
+        android:textStyle="bold"
+        android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+        android:visibility="gone"
+        tools:text="1 selected" />
+
+    <Spinner
+        style="@style/ActionModeSpinner"
+        android:id="@+id/selection_spinner"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/res/layout/file_list_activity.xml b/res/layout/file_list_activity.xml
new file mode 100644
index 0000000..98c6e11
--- /dev/null
+++ b/res/layout/file_list_activity.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <include
+        android:id="@+id/player_panel"
+        layout="@layout/player_panel"
+        android:visibility="gone"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/file_list_fragment.xml b/res/layout/file_list_fragment.xml
new file mode 100644
index 0000000..5031fbd
--- /dev/null
+++ b/res/layout/file_list_fragment.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.android.soundrecorder.filelist.FileListRecyclerView
+        android:id="@+id/recycler_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipToPadding="false"
+        android:paddingTop="@dimen/file_list_padding_top"
+        android:scrollbars="vertical" />
+
+    <FrameLayout
+        android:id="@+id/list_empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center">
+
+        <TextView
+            style="@android:style/TextAppearance.Medium"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:text="@string/file_list_empty" />
+    </FrameLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/file_list_item_folder.xml b/res/layout/file_list_item_folder.xml
new file mode 100644
index 0000000..717a434
--- /dev/null
+++ b/res/layout/file_list_item_folder.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/file_list_folder_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true"
+    android:minHeight="@dimen/file_list_item_height">
+
+    <ImageView
+        android:id="@+id/list_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:src="@drawable/file_list_folder" />
+
+    <FrameLayout
+        android:id="@+id/list_check_box_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:paddingEnd="@dimen/list_item_checkbox_padding"
+        android:paddingStart="@dimen/list_item_checkbox_padding">
+        <CheckBox
+            android:id="@+id/list_check_box"
+            style="@android:style/Widget.DeviceDefault.CompoundButton.CheckBox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:visibility="invisible" />
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/list_item_title"
+        style="@android:style/TextAppearance.Medium"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@id/list_icon"
+        android:layout_toStartOf="@id/list_check_box_layout"
+        android:singleLine="true"
+        tools:text="Folder name" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/file_list_item_recording.xml b/res/layout/file_list_item_recording.xml
new file mode 100644
index 0000000..e967719
--- /dev/null
+++ b/res/layout/file_list_item_recording.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/file_list_recording_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true"
+    android:minHeight="@dimen/file_list_item_height">
+
+    <ImageView
+        android:id="@+id/list_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:src="@drawable/media_play" />
+
+    <FrameLayout
+        android:id="@+id/list_check_box_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:paddingEnd="@dimen/list_item_checkbox_padding"
+        android:paddingStart="@dimen/list_item_checkbox_padding">
+        <CheckBox
+            android:id="@+id/list_check_box"
+            style="@android:style/Widget.DeviceDefault.CompoundButton.CheckBox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:visibility="invisible" />
+
+        <com.android.soundrecorder.filelist.ui.WaveIndicator
+            android:id="@+id/list_wave_indicator"
+            android:layout_width="@dimen/player_indicator_width"
+            android:layout_height="@dimen/player_indicator_height"
+            android:layout_gravity="center"
+            android:visibility="gone" />
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toEndOf="@id/list_icon"
+        android:layout_toStartOf="@id/list_check_box_layout"
+        android:gravity="center_vertical"
+        android:layout_centerVertical="true"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/list_item_title"
+            style="@android:style/TextAppearance.Medium"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            tools:text="Title" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/list_item_date_modified"
+                style="@android:style/TextAppearance.Small"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                tools:text="Date modified" />
+
+            <TextView
+                android:id="@+id/list_item_duration"
+                style="@android:style/TextAppearance.Small"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+                android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+                tools:text="Duration" />
+        </LinearLayout>
+    </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/main.xml b/res/layout/main.xml
new file mode 100644
index 0000000..35db5c2
--- /dev/null
+++ b/res/layout/main.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/main_background">
+
+    <include layout="@layout/timer_view_layout" />
+
+    <include layout="@layout/media_button_panel" />
+</RelativeLayout>
diff --git a/res/layout/media_button_panel.xml b/res/layout/media_button_panel.xml
new file mode 100644
index 0000000..261ca3f
--- /dev/null
+++ b/res/layout/media_button_panel.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/MediaPanelStyle">
+
+    <ImageButton
+        android:id="@+id/listButton"
+        style="@style/MediaButtonSmallRight"
+        android:src="@drawable/media_list" />
+
+    <ImageButton
+        android:id="@+id/recordButton"
+        style="@style/MediaButton"
+        android:elevation="@dimen/media_record_button_elevation"
+        android:src="@drawable/record" />
+
+    <ImageButton
+        android:id="@+id/stopButton"
+        style="@style/MediaButtonSmallLeft"
+        android:src="@drawable/media_stop" />
+</LinearLayout>
diff --git a/res/layout/player_panel.xml b/res/layout/player_panel.xml
new file mode 100644
index 0000000..160d3fb
--- /dev/null
+++ b/res/layout/player_panel.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<com.android.soundrecorder.filelist.player.PlayerPanel
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/player_panel_layout"
+    style="@style/PlayerPanelLayout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_alignParentBottom="true"
+    android:background="@color/main_background"
+    android:elevation="6dp"
+    android:orientation="vertical">
+
+    <ProgressBar
+        android:id="@+id/player_progress"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/player_panel_progress_bar_height"
+        android:layout_marginBottom="@dimen/player_panel_progress_bar_margin_bottom"
+        android:max="100"
+        tools:progress="50" />
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/player_panel_margin_horizontal"
+        android:layout_marginEnd="@dimen/player_panel_margin_horizontal"
+        android:layout_marginBottom="@dimen/player_panel_progress_text_margin_bottom">
+        <TextView
+            android:id="@+id/player_progress_time"
+            style="@android:style/TextAppearance.Small"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:gravity="start|center_vertical" />
+
+        <TextView
+            android:id="@+id/player_total_time"
+            style="@android:style/TextAppearance.Small"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end|center_vertical"
+            android:gravity="end|center_vertical" />
+    </FrameLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/player_panel_margin_horizontal"
+        android:layout_marginEnd="@dimen/player_panel_margin_horizontal">
+
+        <ImageButton
+            android:id="@+id/player_controller"
+            style="@style/PlayerPanelMediaButton"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:src="@drawable/player_panel_play" />
+
+        <TextView
+            android:id="@+id/player_title"
+            style="@android:style/TextAppearance.Medium"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"
+            android:layout_toStartOf="@id/player_controller"
+            android:singleLine="true"
+            tools:text="Title" />
+    </RelativeLayout>
+
+</com.android.soundrecorder.filelist.player.PlayerPanel>
\ No newline at end of file
diff --git a/res/layout/rename_dialog.xml b/res/layout/rename_dialog.xml
new file mode 100644
index 0000000..d3213ae
--- /dev/null
+++ b/res/layout/rename_dialog.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <EditText
+        android:id="@+id/rename_edit_text"
+        style="@android:style/Widget.DeviceDefault.EditText"
+        android:inputType="text"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginEnd="@dimen/rename_edit_text_margin"
+        android:layout_marginStart="@dimen/rename_edit_text_margin"
+        android:layout_marginTop="@dimen/rename_edit_text_margin"
+        android:singleLine="true" />
+
+    <FrameLayout
+        android:id="@+id/rename_info_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginEnd="@dimen/rename_info_text_margin_horizontal"
+        android:layout_marginStart="@dimen/rename_info_text_margin_horizontal"
+        android:visibility="gone"
+        tools:visibility="visible">
+
+        <TextView
+            android:id="@+id/rename_info_text"
+            style="@android:style/TextAppearance.Material.Caption"
+            android:textColor="?android:attr/colorAccent"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:layout_gravity="center_vertical"
+            tools:text="@string/rename_dialog_info_exists"/>
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/dialog_warning"
+            android:layout_gravity="center_vertical|end"/>
+    </FrameLayout>
+</LinearLayout>
diff --git a/res/layout/spinner_dropdown_item.xml b/res/layout/spinner_dropdown_item.xml
new file mode 100644
index 0000000..7c78653
--- /dev/null
+++ b/res/layout/spinner_dropdown_item.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    style="?android:attr/spinnerDropDownItemStyle"
+    android:fontFamily="sans-serif-medium"
+    android:gravity="start|center_vertical"
+    android:singleLine="true"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/action_mode_spinner_item_height"
+    android:paddingStart="@dimen/action_mode_spinner_padding"
+    android:paddingEnd="@dimen/action_mode_spinner_padding"
+    android:ellipsize="marquee"/>
\ No newline at end of file
diff --git a/res/layout/timer_view_layout.xml b/res/layout/timer_view_layout.xml
new file mode 100644
index 0000000..b0c9250
--- /dev/null
+++ b/res/layout/timer_view_layout.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/timerViewLayout"
+    style="@style/TimerViewLayout"
+    android:background="@color/vumeter_background_color">
+
+    <RelativeLayout
+        style="@style/VUMeterLayoutStyle">
+
+        <com.android.soundrecorder.VUMeter
+                android:id="@+id/uvMeter"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_centerInParent="true"/>
+    </RelativeLayout>
+
+    <TextView
+        android:id="@+id/timerView"
+        style="@style/TimerTextStyle" />
+
+    <LinearLayout
+        android:id="@+id/stateMessage2Layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/timerView"
+        android:layout_centerHorizontal="true"
+        android:baselineAligned="false"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/stateMessage2"
+            style="@android:style/TextAppearance.Medium"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="5px"
+            android:textSize="@dimen/message_text_size"
+            android:text="@string/recording" />
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/stateMessage1"
+        style="@android:style/TextAppearance.Small"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/stateMessage2Layout"
+        android:layout_centerHorizontal="true" />
+</RelativeLayout>
diff --git a/res/menu/list_action_mode_menu.xml b/res/menu/list_action_mode_menu.xml
new file mode 100644
index 0000000..fde6203
--- /dev/null
+++ b/res/menu/list_action_mode_menu.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context=".filelist.FileListActivity">
+    <item
+        android:id="@+id/action_edit"
+        android:icon="@drawable/action_mode_edit"
+        android:showAsAction="ifRoom"
+        android:title="@string/action_mode_menu_edit" />
+    <item
+        android:id="@+id/action_share"
+        android:icon="@drawable/action_mode_share"
+        android:showAsAction="always"
+        android:title="@string/action_mode_menu_share" />
+    <item
+        android:id="@+id/action_delete"
+        android:icon="@drawable/action_mode_delete"
+        android:showAsAction="ifRoom"
+        android:title="@string/action_mode_menu_delete" />
+    <item
+        android:id="@+id/action_select_all"
+        android:showAsAction="ifRoom"
+        android:visible="false"
+        android:title="@string/action_mode_select_all" />
+</menu>
diff --git a/res/menu/main_menu.xml b/res/menu/main_menu.xml
new file mode 100644
index 0000000..c38ee69
--- /dev/null
+++ b/res/menu/main_menu.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2015, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/menu_item_filetype"
+        android:title="@string/format_setting"/>
+    <item
+        android:id="@+id/menu_item_storage"
+        android:title="@string/storage_setting"/>
+    <item
+        android:id="@+id/menu_item_keyboard"
+        android:title="@string/keyboard"/>
+</menu>
diff --git a/res/menu/select_all_popup_menu.xml b/res/menu/select_all_popup_menu.xml
new file mode 100644
index 0000000..ae5c708
--- /dev/null
+++ b/res/menu/select_all_popup_menu.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context=".filelist.FileListActivity">
+    <item
+        android:id="@+id/action_select_all"
+        android:showAsAction="always"
+        android:title="@string/action_mode_select_all" />
+</menu>
diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml
new file mode 100644
index 0000000..2234394
--- /dev/null
+++ b/res/values-land/styles.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <style name="MediaButtonSmallLeft" parent="MediaButtonSmall">
+        <item name="android:layout_marginTop">@dimen/media_panel_margin_bottom</item>
+    </style>
+    <style name="MediaButtonSmallRight" parent="MediaButtonSmall">
+        <item name="android:layout_marginBottom">@dimen/media_panel_margin_bottom</item>
+    </style>
+    <style name="MediaPanelStyle">
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_width">@dimen/media_panel_height</item>
+        <item name="android:layout_alignParentEnd">true</item>
+        <item name="android:layout_marginEnd">@dimen/media_panel_margin_right</item>
+        <item name="android:gravity">center</item>
+        <item name="android:orientation">vertical</item>
+    </style>
+    <style name="TimerViewLayout">
+        <item name="android:layout_width">@dimen/timer_view_layout_size</item>
+        <item name="android:layout_height">@dimen/timer_view_layout_size</item>
+        <item name="android:layout_centerVertical">true</item>
+        <item name="android:layout_marginLeft">@dimen/timer_layout_margin_top</item>
+    </style>
+    <style name="VUMeterLayoutStyle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_marginStart">@dimen/vumeter_layout_margin</item>
+        <item name="android:layout_marginTop">@dimen/vumeter_layout_margin</item>
+        <item name="android:layout_marginEnd">@dimen/vumeter_layout_margin</item>
+        <item name="android:layout_marginBottom">@dimen/vumeter_layout_margin_minus</item>
+    </style>
+</resources>
diff --git a/res/values-large/dimens.xml b/res/values-large/dimens.xml
new file mode 100644
index 0000000..c8f5394
--- /dev/null
+++ b/res/values-large/dimens.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <dimen name="action_bar_title_font_size">20sp</dimen>
+    <dimen name="timer_view_layout_size">360dp</dimen>
+    <dimen name="media_panel_height">120dp</dimen>
+    <dimen name="media_panel_margin_right">58dp</dimen>
+    <dimen name="media_panel_margin_bottom">58dp</dimen>
+    <dimen name="media_button_size">72dp</dimen>
+    <dimen name="media_button_size_small">48dp</dimen>
+    <dimen name="media_button_margin">64dp</dimen>
+    <dimen name="media_record_button_elevation">6dp</dimen>
+    <dimen name="message_text_size">18sp</dimen>
+
+    <dimen name="timer_text_size">48dip</dimen>
+    <dimen name="timer_text_margin_top">137dp</dimen>
+    <dimen name="timer_text_margin_bottom">24dp</dimen>
+    <dimen name="timer_layout_margin_top">31dp</dimen>
+    <!-- minus status bar height (24dp) in landscape. -->
+    <dimen name="vumeter_layout_margin_minus">11dp</dimen>
+    <dimen name="vumeter_layout_margin">35dp</dimen>
+    <dimen name="vumeter_outer_width">2dp</dimen>
+    <dimen name="vumeter_outer_inner_margin">20dp</dimen>
+    <dimen name="vumeter_progress_width">6dp</dimen>
+    <dimen name="vumeter_progress_dashed_width">4dp</dimen>
+</resources>
diff --git a/res/values-normal/dimens.xml b/res/values-normal/dimens.xml
new file mode 100644
index 0000000..c8f5394
--- /dev/null
+++ b/res/values-normal/dimens.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <dimen name="action_bar_title_font_size">20sp</dimen>
+    <dimen name="timer_view_layout_size">360dp</dimen>
+    <dimen name="media_panel_height">120dp</dimen>
+    <dimen name="media_panel_margin_right">58dp</dimen>
+    <dimen name="media_panel_margin_bottom">58dp</dimen>
+    <dimen name="media_button_size">72dp</dimen>
+    <dimen name="media_button_size_small">48dp</dimen>
+    <dimen name="media_button_margin">64dp</dimen>
+    <dimen name="media_record_button_elevation">6dp</dimen>
+    <dimen name="message_text_size">18sp</dimen>
+
+    <dimen name="timer_text_size">48dip</dimen>
+    <dimen name="timer_text_margin_top">137dp</dimen>
+    <dimen name="timer_text_margin_bottom">24dp</dimen>
+    <dimen name="timer_layout_margin_top">31dp</dimen>
+    <!-- minus status bar height (24dp) in landscape. -->
+    <dimen name="vumeter_layout_margin_minus">11dp</dimen>
+    <dimen name="vumeter_layout_margin">35dp</dimen>
+    <dimen name="vumeter_outer_width">2dp</dimen>
+    <dimen name="vumeter_outer_inner_margin">20dp</dimen>
+    <dimen name="vumeter_progress_width">6dp</dimen>
+    <dimen name="vumeter_progress_dashed_width">4dp</dimen>
+</resources>
diff --git a/res/values-port/styles.xml b/res/values-port/styles.xml
new file mode 100644
index 0000000..7d82c0d
--- /dev/null
+++ b/res/values-port/styles.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <style name="MediaButtonSmallLeft" parent="MediaButtonSmall">
+        <item name="android:layout_marginStart">@dimen/media_button_margin</item>
+    </style>
+    <style name="MediaButtonSmallRight" parent="MediaButtonSmall">
+        <item name="android:layout_marginEnd">@dimen/media_button_margin</item>
+    </style>
+    <style name="MediaPanelStyle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">@dimen/media_panel_height</item>
+        <item name="android:layout_alignParentBottom">true</item>
+        <item name="android:layout_marginBottom">@dimen/media_panel_margin_bottom</item>
+        <item name="android:gravity">center</item>
+        <item name="android:orientation">horizontal</item>
+        <item name="android:layoutDirection">rtl</item>
+    </style>
+    <style name="TimerViewLayout">
+        <item name="android:layout_width">@dimen/timer_view_layout_size</item>
+        <item name="android:layout_height">@dimen/timer_view_layout_size</item>
+        <item name="android:layout_centerHorizontal">true</item>
+        <item name="android:layout_marginTop">@dimen/timer_layout_margin_top</item>
+    </style>
+    <style name="VUMeterLayoutStyle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_margin">@dimen/vumeter_layout_margin</item>
+    </style>
+</resources>
diff --git a/res/values-small/dimens.xml b/res/values-small/dimens.xml
new file mode 100644
index 0000000..0e8b8c8
--- /dev/null
+++ b/res/values-small/dimens.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <dimen name="action_bar_title_font_size">14sp</dimen>
+    <dimen name="timer_view_layout_size">225dp</dimen>
+    <dimen name="media_panel_height">96dp</dimen>
+    <dimen name="media_panel_margin_right">16dp</dimen>
+    <dimen name="media_panel_margin_bottom">16dp</dimen>
+    <dimen name="media_button_size">72dp</dimen>
+    <dimen name="media_button_size_small">48dp</dimen>
+    <dimen name="media_button_margin">48dp</dimen>
+    <dimen name="media_record_button_elevation">6dp</dimen>
+
+    <dimen name="timer_text_size">36sp</dimen>
+    <dimen name="timer_text_margin_top">86dp</dimen>
+    <dimen name="timer_text_margin_bottom">4dp</dimen>
+    <dimen name="timer_layout_margin_top">4dp</dimen>
+    <dimen name="message_text_size">14sp</dimen>
+
+    <!-- minus status bar height (24dp) in landscape. -->
+    <dimen name="vumeter_layout_margin_minus">11dp</dimen>
+    <dimen name="vumeter_layout_margin">20dp</dimen>
+    <dimen name="vumeter_outer_width">2dp</dimen>
+    <dimen name="vumeter_outer_inner_margin">20dp</dimen>
+    <dimen name="vumeter_progress_width">6dp</dimen>
+    <dimen name="vumeter_progress_dashed_width">4dp</dimen>
+</resources>
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
new file mode 100644
index 0000000..c8f5394
--- /dev/null
+++ b/res/values-xlarge/dimens.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <dimen name="action_bar_title_font_size">20sp</dimen>
+    <dimen name="timer_view_layout_size">360dp</dimen>
+    <dimen name="media_panel_height">120dp</dimen>
+    <dimen name="media_panel_margin_right">58dp</dimen>
+    <dimen name="media_panel_margin_bottom">58dp</dimen>
+    <dimen name="media_button_size">72dp</dimen>
+    <dimen name="media_button_size_small">48dp</dimen>
+    <dimen name="media_button_margin">64dp</dimen>
+    <dimen name="media_record_button_elevation">6dp</dimen>
+    <dimen name="message_text_size">18sp</dimen>
+
+    <dimen name="timer_text_size">48dip</dimen>
+    <dimen name="timer_text_margin_top">137dp</dimen>
+    <dimen name="timer_text_margin_bottom">24dp</dimen>
+    <dimen name="timer_layout_margin_top">31dp</dimen>
+    <!-- minus status bar height (24dp) in landscape. -->
+    <dimen name="vumeter_layout_margin_minus">11dp</dimen>
+    <dimen name="vumeter_layout_margin">35dp</dimen>
+    <dimen name="vumeter_outer_width">2dp</dimen>
+    <dimen name="vumeter_outer_inner_margin">20dp</dimen>
+    <dimen name="vumeter_progress_width">6dp</dimen>
+    <dimen name="vumeter_progress_dashed_width">4dp</dimen>
+</resources>
diff --git a/res/values-xxxhdpi/dimens.xml b/res/values-xxxhdpi/dimens.xml
new file mode 100644
index 0000000..c8f5394
--- /dev/null
+++ b/res/values-xxxhdpi/dimens.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <dimen name="action_bar_title_font_size">20sp</dimen>
+    <dimen name="timer_view_layout_size">360dp</dimen>
+    <dimen name="media_panel_height">120dp</dimen>
+    <dimen name="media_panel_margin_right">58dp</dimen>
+    <dimen name="media_panel_margin_bottom">58dp</dimen>
+    <dimen name="media_button_size">72dp</dimen>
+    <dimen name="media_button_size_small">48dp</dimen>
+    <dimen name="media_button_margin">64dp</dimen>
+    <dimen name="media_record_button_elevation">6dp</dimen>
+    <dimen name="message_text_size">18sp</dimen>
+
+    <dimen name="timer_text_size">48dip</dimen>
+    <dimen name="timer_text_margin_top">137dp</dimen>
+    <dimen name="timer_text_margin_bottom">24dp</dimen>
+    <dimen name="timer_layout_margin_top">31dp</dimen>
+    <!-- minus status bar height (24dp) in landscape. -->
+    <dimen name="vumeter_layout_margin_minus">11dp</dimen>
+    <dimen name="vumeter_layout_margin">35dp</dimen>
+    <dimen name="vumeter_outer_width">2dp</dimen>
+    <dimen name="vumeter_outer_inner_margin">20dp</dimen>
+    <dimen name="vumeter_progress_width">6dp</dimen>
+    <dimen name="vumeter_progress_dashed_width">4dp</dimen>
+</resources>
diff --git a/res/values-zh-rCN/codeaurora_strings.xml b/res/values-zh-rCN/codeaurora_strings.xml
new file mode 100644
index 0000000..6241daf
--- /dev/null
+++ b/res/values-zh-rCN/codeaurora_strings.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Prefix of recording file name, like "Rec", default value is null -->
+    <string name="def_save_name_prefix" translatable="false">新录音</string>
+
+    <string name="save_dialog_title">要保存录音吗?</string>
+    <string name="rename_dialog_title">重命名</string>
+    <string name="rename_dialog_info_null">文件名不能为空</string>
+    <string name="rename_dialog_info_exists">文件名已经存在</string>
+    <string name="rename_dialog_info_length_limit">已经达到输入上限</string>
+    <string name="rename_dialog_save">保存</string>
+
+    <string name="delete_dialog_confirm">删除</string>
+    <plurals name="delete_selection_message">
+        <item quantity="one">要删除所选内容吗?</item>
+        <item quantity="other">要删除所选内容吗?</item>
+    </plurals>
+    <string name="share_title">分享</string>
+    <string name="file_list_activity_label">录音列表</string>
+    <string name="file_list_empty">没有录音</string>
+    <string name="file_list_FM">收音机录音</string>
+    <string name="file_list_call">电话录音</string>
+    <string name="action_mode_menu_edit">编辑</string>
+    <string name="action_mode_menu_share">分享</string>
+    <string name="action_mode_menu_delete">删除</string>
+    <string name="action_mode_selected">选中了 %1$d 项</string>
+    <string name="action_mode_select_all">全选</string>
+    <string name="action_mode_deselect_all">取消全选</string>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
old mode 100644
new mode 100755
index 2e5ca50..3d6e3a9
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -20,7 +20,8 @@
     <string name="record_your_message" msgid="1583671906691662148">"录下您的留言"</string>
     <string name="message_recorded" msgid="381539460921872233">"信息已录制"</string>
     <string name="review_message" msgid="201616012287839474">"查看信息"</string>
-    <string name="recording" msgid="4426791467211376099">"录音"</string>
+    <string name="recording" msgid="4426791467211376099">"录音中"</string>
+    <string name="recording_paused">暂停</string>
     <string name="recording_stopped" msgid="1580278719585249612">"录音停止"</string>
     <string name="storage_is_full" msgid="416039755956726221">"存储空间已满"</string>
     <string name="max_length_reached" msgid="6398215743584093353">"已达到长度上限"</string>
@@ -31,6 +32,9 @@
     <string name="accept" msgid="1888285552128743656">"完成"</string>
     <string name="discard" msgid="761613433029406319">"放弃"</string>
     <string name="button_ok" msgid="3004911343723076226">"确定"</string>
+    <string name="press_record">点击开始录音: 0-MIC 1-Rx 2-Tx+Rx 3-AMR 4-EVRC 5-QCELP 6-AAC_MP4 9-AMR-WB A-AMR-WB-3GPP</string>
+    <string name="press_record2">点击按键录音</string>
+
     <!-- no translation found for audio_db_title_format (7912182366970749312) -->
     <skip />
     <string name="audio_db_artist_name" msgid="961640229118120080">"您的录音"</string>
@@ -38,6 +42,26 @@
     <string name="audio_db_playlist_name" msgid="5592939050047058162">"我的录音"</string>
     <string name="error_sdcard_access" product="nosdcard" msgid="86629081075681774">"无法使用USB存储设备。"</string>
     <string name="error_sdcard_access" product="default" msgid="5750308258096153274">"无法使用SD卡。"</string>
+	<string name="error_record_interrupted">"录音中断。"</string>
     <string name="error_app_internal" msgid="312884382388702022">"发生内部应用错误。"</string>
     <string name="error_mediadb_new_record" msgid="261714902333432462">"无法保存录制的音频。"</string>
+    <!-- shown as the dialog title when user selects recording format -->
+    <string name="format_setting">"文件类型"</string>
+    <string name="format_setting_aac_item">"AAC"</string>
+    <string name="format_setting_amr_item">"AMR"</string>
+    <string name="format_setting_3gpp_item">"3GPP"</string>
+    <string name="format_setting_wav_item">"WAV"</string>
+    <string name="storage_setting">"存储位置"</string>
+    <string name="storage_setting_sdcard_item">"SD卡"</string>
+    <string name="storage_setting_local_item">"话机U盘"</string>
+    <string name="no_phonestorage">"手机内存储被卸载"</string>
+    <string name="keyboard">键盘</string>
+    <string name="file_deleted">文件已经被删除 !</string>
+    <string name="in_call_record_error">通话中录音失败.</string>
+    <string name="recording_now">正在录音 ...</string>
+    <string name="file_saved">文件保存成功</string>
+    <string name="file_saved_interrupt">录音已停止并保存</string>
+    <string name="file_discard">放弃录音</string>
+    <string name="title_please_wait">请等待...</string>
+    <string name="msg_starting_recording">正在启动录音</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 204e611..478ec6f 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -38,6 +38,7 @@
     <string name="audio_db_playlist_name" msgid="5592939050047058162">"我的錄音"</string>
     <string name="error_sdcard_access" product="nosdcard" msgid="86629081075681774">"無法存取 USB 儲存裝置。"</string>
     <string name="error_sdcard_access" product="default" msgid="5750308258096153274">"無法存取 SD 記憶卡。"</string>
+	<string name="error_record_interrupted">"錄音中斷。"</string>
     <string name="error_app_internal" msgid="312884382388702022">"內部應用程式錯誤。"</string>
     <string name="error_mediadb_new_record" msgid="261714902333432462">"無法儲存預先錄製的語音訊息"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index d1b50d9..d9cce71 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -38,6 +38,7 @@
     <string name="audio_db_playlist_name" msgid="5592939050047058162">"我的錄音"</string>
     <string name="error_sdcard_access" product="nosdcard" msgid="86629081075681774">"無法存取 USB 儲存裝置。"</string>
     <string name="error_sdcard_access" product="default" msgid="5750308258096153274">"無法存取 SD 卡。"</string>
+	<string name="error_record_interrupted">"錄音中斷。"</string>
     <string name="error_app_internal" msgid="312884382388702022">"內部應用程式錯誤。"</string>
     <string name="error_mediadb_new_record" msgid="261714902333432462">"無法儲存錄製的音訊。"</string>
 </resources>
diff --git a/res/values/codeaurora_colors.xml b/res/values/codeaurora_colors.xml
new file mode 100644
index 0000000..1348665
--- /dev/null
+++ b/res/values/codeaurora_colors.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <color name="list_activity_color_primary">#EF5350</color>
+    <color name="list_activity_color_primary_dark">#9B3634</color>
+    <color name="list_activity_color_accent">#EF5350</color>
+    <color name="action_mode_color_primary">#FFFFFF</color>
+    <color name="action_mode_color_primary_dark">#FFFFFF</color>
+</resources>
diff --git a/res/values/codeaurora_dimens.xml b/res/values/codeaurora_dimens.xml
new file mode 100644
index 0000000..02c3cef
--- /dev/null
+++ b/res/values/codeaurora_dimens.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <dimen name="rename_edit_text_margin">24dp</dimen>
+    <dimen name="rename_info_text_margin_horizontal">30dp</dimen>
+    <dimen name="file_list_item_height">72dp</dimen>
+    <dimen name="file_list_padding_top">8dp</dimen>
+    <dimen name="list_item_checkbox_padding">8dp</dimen>
+
+    <dimen name="player_indicator_width">24dp</dimen>
+    <dimen name="player_indicator_height">24dp</dimen>
+
+    <dimen name="player_panel_margin_horizontal">16dp</dimen>
+    <dimen name="player_panel_margin_bottom">8dp</dimen>
+    <dimen name="player_panel_progress_bar_height">2dp</dimen>
+    <dimen name="player_panel_progress_bar_margin_bottom">8dp</dimen>
+    <dimen name="player_panel_progress_text_margin_bottom">9dp</dimen>
+    <dimen name="player_panel_media_button_size">32dp</dimen>
+
+    <dimen name="action_mode_spinner_width">147dp</dimen>
+    <dimen name="action_mode_spinner_item_height">48dp</dimen>
+    <dimen name="action_mode_spinner_padding">6dp</dimen>
+</resources>
diff --git a/res/values/codeaurora_strings.xml b/res/values/codeaurora_strings.xml
new file mode 100644
index 0000000..b0fdfea
--- /dev/null
+++ b/res/values/codeaurora_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Prefix of recording file name, like "Rec", default value is null -->
+    <string name="def_save_name_prefix" translatable="false">New record</string>
+
+    <string name="save_dialog_title">Save recording?</string>
+    <string name="rename_dialog_title">Rename</string>
+    <string name="rename_dialog_info_null">File name can\'t be null.</string>
+    <string name="rename_dialog_info_exists">The name already exists.</string>
+    <string name="rename_dialog_info_length_limit">Maximum character limit reached.</string>
+    <string name="rename_dialog_save">Save</string>
+
+    <string name="delete_dialog_confirm">Delete</string>
+    <plurals name="delete_selection_message">
+        <item quantity="one">Delete selected item?</item>
+        <item quantity="other">Delete selected items?</item>
+    </plurals>
+    <string name="share_title">Share</string>
+    <string name="file_list_activity_label">Recording list</string>
+    <string name="file_list_empty">No recording</string>
+    <string name="file_list_FM">FM Recording</string>
+    <string name="file_list_call">Call Recording</string>
+
+    <string name="list_item_date_modified_format" translatable="false">
+        <xliff:g id="format">yy/MM/dd HH:mm:ss</xliff:g>
+    </string>
+
+    <string name="action_mode_menu_edit">Edit</string>
+    <string name="action_mode_menu_share">Share</string>
+    <string name="action_mode_menu_delete">Delete</string>
+    <string name="action_mode_selected">%1$d selected</string>
+    <string name="action_mode_select_all">Select all</string>
+    <string name="action_mode_deselect_all">Deselect all</string>
+</resources>
diff --git a/res/values/codeaurora_styles.xml b/res/values/codeaurora_styles.xml
new file mode 100644
index 0000000..7f8691e
--- /dev/null
+++ b/res/values/codeaurora_styles.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <style name="FileListActivity" parent="android:Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:windowActionBar">true</item>
+        <item name="android:windowNoTitle">false</item>
+        <item name="android:actionBarStyle">@style/FileListActionBar</item>
+        <item name="android:colorAccent">@color/list_activity_color_accent</item>
+        <item name="android:colorPrimary">@color/list_activity_color_primary</item>
+        <item name="android:colorPrimaryDark">@color/list_activity_color_primary_dark</item>
+
+        <item name="android:homeAsUpIndicator">@drawable/action_bar_back</item>
+
+        <item name="android:actionModeBackground">@color/action_mode_color_primary</item>
+        <item name="android:actionModeCloseDrawable">@drawable/action_mode_back</item>
+    </style>
+
+    <style name="FileListActionBar" parent="android:Widget.DeviceDefault.ActionBar.Solid">
+        <item name="android:titleTextStyle">@style/ListActionBarTitle</item>
+        <item name="android:displayOptions">homeAsUp|showTitle</item>
+    </style>
+
+    <style name="ListActionBarTitle"
+        parent="android:TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse">
+        <item name="android:textSize">@dimen/action_bar_title_font_size</item>
+    </style>
+
+    <style name="PlayerPanelLayout">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:paddingBottom">@dimen/player_panel_margin_bottom</item>
+    </style>
+
+    <style name="PlayerPanelMediaButton" parent="MediaButtonSmall">
+        <item name="android:layout_width">@dimen/player_panel_media_button_size</item>
+        <item name="android:layout_height">@dimen/player_panel_media_button_size</item>
+    </style>
+
+    <style name="ActionModeSpinner" parent="android:Widget.DeviceDefault.Spinner">
+        <item name="android:dropDownWidth">@dimen/action_mode_spinner_width</item>
+    </style>
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 0000000..eade503
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <color name="color_primary_dark">#A2A2A2</color>
+    <color name="color_accent">#EF5350</color>
+    <color name="main_background">#FFFAFAFA</color>
+    <color name="button_tint_color">#E2E2E2</color>
+    <color name="text_font_color">#8A000000</color>
+
+    <color name="vumeter_background_color">@color/main_background</color>
+    <color name="vumeter_color">#D3D3D3</color>
+    <color name="vumeter_progress_color">#EF5350</color>
+
+    <color name="negative_button_color">#DE000000</color>
+</resources>
diff --git a/res/values/customize.xml b/res/values/customize.xml
new file mode 100644
index 0000000..8a1b5bb
--- /dev/null
+++ b/res/values/customize.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2015, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above
+       copyright notice, this list of conditions and the following
+       disclaimer in the documentation and/or other materials provided
+       with the distribution.
+     * Neither the name of The Linux Foundation nor the names of its
+       contributors may be used to endorse or promote products derived
+       from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Prefix of recording file name, like "Rec", default value is null -->
+    <string name="def_save_name_prefix" translatable="false"></string>
+
+    <!-- Format of recording file name, like "yyyy-MM-dd-HH-mm-ss", default value is "yyyy-MM-dd-HH-mm-ss" -->
+    <string name="def_save_name_format" translatable="false">yyyy-MM-dd-HH-mm-ss</string>
+
+    <!-- Type of saved recording file, the value is 0 for amr, 1 for 3gpp, 2 for wav, default value is 0 -->
+    <integer name="def_save_type" translatable="false">0</integer>
+
+    <!-- Mimetype of saved recording file, the value is "audio/amr", "audio/3gpp", "audio/wave_2ch_lpcm", default value is "audio/amr" -->
+    <string name="def_save_mimetype" translatable="false">audio/amr</string>
+
+    <bool name= "config_storage_path">false</bool>
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..5fe00c3
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<resources>
+    <dimen name="action_bar_title_font_size">14sp</dimen>
+    <dimen name="timer_view_layout_size">225dp</dimen>
+    <dimen name="media_panel_height">96dp</dimen>
+    <dimen name="media_panel_margin_right">16dp</dimen>
+    <dimen name="media_panel_margin_bottom">26dp</dimen>
+    <dimen name="media_button_size">72dp</dimen>
+    <dimen name="media_button_size_small">48dp</dimen>
+    <dimen name="media_button_margin">48dp</dimen>
+    <dimen name="media_record_button_elevation">6dp</dimen>
+
+    <dimen name="timer_text_size">36sp</dimen>
+    <dimen name="timer_text_margin_top">86dp</dimen>
+    <dimen name="timer_text_margin_bottom">4dp</dimen>
+    <dimen name="timer_layout_margin_top">4dp</dimen>
+    <dimen name="message_text_size">14sp</dimen>
+
+    <!-- minus status bar height (24dp) in landscape. -->
+    <dimen name="vumeter_layout_margin_minus">11dp</dimen>
+    <dimen name="vumeter_layout_margin">20dp</dimen>
+    <dimen name="vumeter_outer_width">2dp</dimen>
+    <dimen name="vumeter_outer_inner_margin">20dp</dimen>
+    <dimen name="vumeter_progress_width">6dp</dimen>
+    <dimen name="vumeter_progress_dashed_width">4dp</dimen>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
old mode 100644
new mode 100755
index f24c208..5ade3cf
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -17,7 +17,7 @@
 <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>
-
+    <string name="folder_name">Recording</string>
     <!-- Screen title before and during recording -->
     <string name="record_your_message">Record your message</string>
     <!-- Screen title after recording -->
@@ -26,6 +26,8 @@
     <string name="review_message">Review message</string>
     <!-- Label shown during recording -->
     <string name="recording">Recording</string>
+    <!-- Label shown when recording is paused -->
+    <string name="recording_paused">Pause</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 -->
@@ -49,6 +51,17 @@
     <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" translatable="false"><xliff:g id="format">%02d:%02d</xliff:g></string>
+
+    <string name="timer_format_hour" translatable="false">
+        <xliff:g id="format">%d:%02d:%02d</xliff:g>
+    </string>
+    <!-- label shown before the user has recorded anything -->
+    <string name="press_record">Press: 0-MIC 1-Rx 2-Tx+Rx 3-AMR 4-EVRC 5-QCELP
+    6-AAC_MP4 9-AMR-WB A-AMR-WB-3GPP and Press record</string>
+    <string name="press_record2">Press Record</string>
+    <!-- label shown before the user has recorded anything -SSR support added-->
+    <string name="press_record_ssr">Press: 0-MIC 1-Rx 2-Tx+Rx 3-AMR 4-EVRC 5-QCELP
+    6-AAC_MP4 7-AAC_SSR 8-LPCM_SSR 9-AMR-WB A-AMR-WB-3GPP and 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>
@@ -63,8 +76,38 @@
     <string name="error_sdcard_access" product="nosdcard">Can\'t access USB storage.</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" product="default">Can\'t access SD card.</string>
+    <!-- shown as the message in a dialog when the recording is interrupted internally -->
+    <string name="error_record_interrupted">Recording interrupted</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>
+    <string name="error_app_internal">Internal application error</string>
+    <!-- shown as the message in a dialog when the app encountered and unsupported format -->
+    <string name="error_app_unsupported">Unsupported format</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">Couldn\'t save recorded audio.</string>
+    <!-- shown as the message in a dialog when the recording cannot be started while in call -->
+    <string name="error_mediadb_incall">This option is supported, only while in call</string>
+    <!-- shown as the message in a dialog when voice call recording is started for AAC format -->
+    <string name="error_mediadb_aacincall">AAC voice call recording is not supported</string>
+    <!-- shown as the dialog title when user selects recording format -->
+    <string name="format_setting">File type</string>
+    <string name="format_setting_aac_item">AAC</string>
+    <string name="format_setting_amr_item">AMR</string>
+    <string name="format_setting_3gpp_item">3GPP</string>
+    <string name="format_setting_wav_item">WAV</string>
+    <string name="storage_setting">Storage location</string>
+    <string name="storage_setting_sdcard_item">SD card</string>
+    <string name="storage_setting_local_item">Phone storage</string>
+    <string name="no_phonestorage">Phone storage unmounted</string>
+    <string name="keyboard">Keyboard</string>
+    <string name="file_deleted">File has been deleted!</string>
+    <string name="in_call_record_error">In call record error.</string>
+    <!-- Label shown during recording -->
+    <string name="recording_now">Recording now ...</string>
+    <!-- shown as the message in a dialog when the recording file is saved successfully -->
+    <string name="file_saved">File has been saved</string>
+    <string name="file_saved_interrupt">Recording has been stopped and saved</string>
+    <!-- shown as the message in a dialog when the recording file is discarded -->
+    <string name="file_discard">File has been discarded</string>
+    <string name="title_please_wait">Please wait... </string>
+    <string name="msg_starting_recording">Starting recording</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1a541c9..000a630 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -14,10 +14,28 @@
      limitations under the License.
 -->
 <resources>
-    <style name="Theme.SoundRecorder" parent="@android:style/Theme.Holo.DialogWhenLarge">
+    <style name="Theme.SoundRecorder" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
         <item name="android:windowCloseOnTouchOutside">true</item>
+        <item name="android:colorPrimaryDark">@color/color_primary_dark</item>
+        <item name="android:colorAccent">@color/color_accent</item>
     </style>
-    <style name="MediaButton" parent="@android:style/MediaButton">
-        <item name="android:background">@android:drawable/btn_default</item>
+    <style name="MediaButton">
+        <item name="android:layout_width">@dimen/media_button_size</item>
+        <item name="android:layout_height">@dimen/media_button_size</item>
+        <item name="android:background">@drawable/media_button_background</item>
+    </style>
+    <style name="MediaButtonSmall" parent="MediaButton">
+        <item name="android:layout_width">@dimen/media_button_size_small</item>
+        <item name="android:layout_height">@dimen/media_button_size_small</item>
+    </style>
+    <style name="TimerTextStyle">
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">@dimen/timer_text_margin_top</item>
+        <item name="android:layout_marginBottom">@dimen/timer_text_margin_bottom</item>
+        <item name="android:layout_centerHorizontal">true</item>
+        <item name="android:textSize">@dimen/timer_text_size</item>
+        <item name="android:textColor">@color/text_font_color</item>
     </style>
 </resources>
diff --git a/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/LongArrayWrapper.java b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/LongArrayWrapper.java
new file mode 100644
index 0000000..5bbd3a1
--- /dev/null
+++ b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/LongArrayWrapper.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.wrapper.soundrecorder.util;
+
+import android.util.LongArray;
+
+public class LongArrayWrapper{
+
+    private LongArray mBase = null;
+
+    /*must set mBase before use it */
+    public LongArrayWrapper(LongArray base)
+    {
+        mBase = base;
+    }
+
+    /*must set mBase before use it */
+    public LongArrayWrapper()
+    {
+        mBase = new LongArray();
+    }
+
+    /*must set mBase before use it */
+    public void add(long value) {
+        if(mBase != null){
+            mBase.add(value);
+        }
+    }
+
+    /*must set mBase before use it */
+    public int size() {
+        if(mBase != null){
+            return mBase.size();
+        }else{
+            return 0;
+        }
+    }
+
+    /*must set mBase before use it */
+    public int indexOf(long value) {
+        if(mBase != null){
+            return mBase.indexOf(value);
+        }else{
+            return 0;
+        }
+    }
+
+}
diff --git a/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/MediaRecorderWrapper.java b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/MediaRecorderWrapper.java
new file mode 100644
index 0000000..f1290a2
--- /dev/null
+++ b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/MediaRecorderWrapper.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.wrapper.soundrecorder.util;
+
+import android.media.MediaRecorder;
+
+
+public class MediaRecorderWrapper {
+
+    public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED;
+
+
+    public final class AudioSource {
+
+        private AudioSource() {}
+
+        public final static int AUDIO_SOURCE_INVALID = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
+
+        /** Default audio source **/
+        public static final int DEFAULT = MediaRecorder.AudioSource.DEFAULT;
+
+        /** Microphone audio source */
+        public static final int MIC = MediaRecorder.AudioSource.MIC;
+
+        /** Voice call uplink (Tx) audio source */
+        public static final int VOICE_UPLINK = MediaRecorder.AudioSource.VOICE_UPLINK;
+
+        /** Voice call downlink (Rx) audio source */
+        public static final int VOICE_DOWNLINK = MediaRecorder.AudioSource.VOICE_DOWNLINK;
+
+        /** Voice call uplink + downlink audio source */
+        public static final int VOICE_CALL = MediaRecorder.AudioSource.VOICE_CALL;
+
+        /** Microphone audio source with same orientation as camera if available, the main
+         *  device microphone otherwise */
+        public static final int CAMCORDER = MediaRecorder.AudioSource.CAMCORDER;
+
+        public static final int VOICE_RECOGNITION = MediaRecorder.AudioSource.VOICE_RECOGNITION;
+
+        public static final int VOICE_COMMUNICATION = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
+
+        public static final int REMOTE_SUBMIX = MediaRecorder.AudioSource.REMOTE_SUBMIX;
+
+        public static final int UNPROCESSED = MediaRecorder.AudioSource.UNPROCESSED;
+
+        /**
+         * Audio source for capturing broadcast radio tuner output.
+         */
+        public static final int RADIO_TUNER = MediaRecorder.AudioSource.RADIO_TUNER;
+
+        public static final int HOTWORD = MediaRecorder.AudioSource.HOTWORD;
+    }
+
+    public final class AudioEncoder {
+      /* Do not change these values without updating their counterparts
+       * in include/media/mediarecorder.h!
+       */
+        private AudioEncoder() {}
+        public static final int DEFAULT = MediaRecorder.AudioEncoder.DEFAULT;
+        /** AMR (Narrowband) audio codec */
+        public static final int AMR_NB = MediaRecorder.AudioEncoder.AMR_NB;
+        /** AMR (Wideband) audio codec */
+        public static final int AMR_WB = MediaRecorder.AudioEncoder.AMR_WB;
+        /** AAC Low Complexity (AAC-LC) audio codec */
+        public static final int AAC = MediaRecorder.AudioEncoder.AAC;
+        /** High Efficiency AAC (HE-AAC) audio codec */
+        public static final int HE_AAC = MediaRecorder.AudioEncoder.HE_AAC;
+        /** Enhanced Low Delay AAC (AAC-ELD) audio codec */
+        public static final int AAC_ELD = MediaRecorder.AudioEncoder.AAC_ELD;
+        /** Ogg Vorbis audio codec */
+        public static final int VORBIS = MediaRecorder.AudioEncoder.VORBIS;
+        /**  EVRC audio codec */
+        public static final int EVRC = 10;//MediaRecorder.AudioEncoder.EVRC;
+        /**  QCELP audio codec */
+        public static final int QCELP = 11;//MediaRecorder.AudioEncoder.QCELP;
+        /**  Linear PCM audio codec */
+        public static final int LPCM = 12;//MediaRecorder.AudioEncoder.LPCM;
+    }
+
+    public final class OutputFormat {
+      /* Do not change these values without updating their counterparts
+       * in include/media/mediarecorder.h!
+       */
+        private OutputFormat() {}
+
+        public static final int DEFAULT = MediaRecorder.OutputFormat.DEFAULT;
+        /** 3GPP media file format*/
+        public static final int THREE_GPP = MediaRecorder.OutputFormat.THREE_GPP;
+        /** MPEG4 media file format*/
+        public static final int MPEG_4 = MediaRecorder.OutputFormat.MPEG_4;
+
+        /** The following formats are audio only .aac or .amr formats */
+
+        /**
+         * AMR NB file format
+         *  Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB
+         */
+        public static final int RAW_AMR = MediaRecorder.OutputFormat.RAW_AMR;
+
+        /** AMR NB file format */
+        public static final int AMR_NB = MediaRecorder.OutputFormat.AMR_NB;
+
+        /** AMR WB file format */
+        public static final int AMR_WB = MediaRecorder.OutputFormat.AMR_WB;
+
+        /** AAC ADIF file format */
+        public static final int AAC_ADIF = MediaRecorder.OutputFormat.AAC_ADIF;
+
+        /** AAC ADTS file format */
+        public static final int AAC_ADTS = MediaRecorder.OutputFormat.AAC_ADTS;
+
+        /**  Stream over a socket, limited to a single stream */
+        public static final int OUTPUT_FORMAT_RTP_AVP = MediaRecorder.OutputFormat.OUTPUT_FORMAT_RTP_AVP;
+
+        /** H.264/AAC data encapsulated in MPEG2/TS */
+        public static final int OUTPUT_FORMAT_MPEG2TS = 8;//MediaRecorder.OutputFormat.OUTPUT_FORMAT_MPEG2TS;
+
+        /** VP8/VORBIS data in a WEBM container */
+        public static final int WEBM = MediaRecorder.OutputFormat.WEBM;
+
+        /** QCP file format */
+        public static final int QCP = 20;//MediaRecorder.OutputFormat.QCP;
+
+        /** WAVE media file format*/
+        public static final int WAVE = 21;//MediaRecorder.OutputFormat.WAVE;
+
+    };
+
+}
diff --git a/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/StorageManagerWrapper.java b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/StorageManagerWrapper.java
new file mode 100755
index 0000000..e12b973
--- /dev/null
+++ b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/StorageManagerWrapper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.wrapper.soundrecorder.util;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.util.Log;
+
+import java.io.File;
+
+public class StorageManagerWrapper{
+    private static final String TAG = "StorageManagerWrapper";
+    private static final int VOLUME_SDCARD_INDEX = 1;
+
+    private static StorageManager mStorageManager = null;
+
+    public static StorageManager getStorageManager(Context context) {
+        if (mStorageManager == null) {
+            mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+        }
+        return mStorageManager;
+    }
+
+    public static StorageVolume[] getVolumeList(Context context) {
+        StorageVolume[] volumes = null;
+
+        try {
+            volumes = getStorageManager(context).getVolumeList();
+        } catch (Exception e) {
+            Log.e(TAG, "couldn't talk to MountService", e);
+        }
+        return volumes;
+    }
+
+    public static StorageVolume getStorageVolume(Context context,File file) {
+        return getStorageManager(context).getStorageVolume(file);
+    }
+
+
+    public static String getSdDirectory(Context context) {
+        String sdDirectory = null;
+
+        try {
+            final StorageVolume[] volumes = StorageManagerWrapper.getVolumeList(context);
+            if (volumes.length > VOLUME_SDCARD_INDEX) {
+                StorageVolume volume = volumes[VOLUME_SDCARD_INDEX];
+                if (volume.isRemovable()) {
+                    sdDirectory = volume.getPath();
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "couldn't talk to MountService", e);
+        }
+
+        return sdDirectory;
+    }
+}
diff --git a/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/StorageVolumeWrapper.java b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/StorageVolumeWrapper.java
new file mode 100644
index 0000000..53815a0
--- /dev/null
+++ b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/StorageVolumeWrapper.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.wrapper.soundrecorder.util;
+
+import android.content.Context;
+import android.os.storage.StorageVolume;
+import android.util.Log;
+
+import java.io.File;
+
+public class StorageVolumeWrapper{
+    private static final String TAG = "StorageVolumeWrapper";
+
+    private static StorageVolume mStorageVolume = null;
+
+    /*must set Instance before use this class*/
+    public static StorageVolume getBaseInstance(Context context) {
+        return mStorageVolume;
+    }
+
+    /*must set Instance before use it*/
+    public static void setBaseInstance(StorageVolume base) {
+        mStorageVolume = base;
+    }
+
+    /*must call it to set Instance before use this class*/
+    public static void setBaseInstance(Context context,File file) {
+        StorageVolume vol = StorageManagerWrapper.getStorageVolume(context,file);
+
+        mStorageVolume = vol;
+    }
+
+    public static String getPath() {
+        String path = null;
+
+        if (mStorageVolume != null) {
+            path = mStorageVolume.getPath();
+        }else{
+            Log.e(TAG, "getPath mStorageVolume is null");
+        }
+
+        return path;
+    }
+
+    public static String getState() {
+        String state = null;
+
+        if (mStorageVolume != null) {
+            state = mStorageVolume.getState();
+        }else{
+            Log.e(TAG, "getState mStorageVolume is null");
+        }
+
+        return state;
+    }
+
+}
diff --git a/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/SystemPropertiesWrapper.java b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/SystemPropertiesWrapper.java
new file mode 100644
index 0000000..6e4f4bf
--- /dev/null
+++ b/src-Wrapper/org/codeaurora/wrapper/soundrecorder/util/SystemPropertiesWrapper.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.wrapper.soundrecorder.util;
+
+import android.os.SystemProperties;
+
+public class SystemPropertiesWrapper{
+
+    public static String get(String key, String def) {
+       return SystemProperties.get(key,def);
+    }
+
+    public static boolean getBoolean(String key, boolean def) {
+        return SystemProperties.getBoolean(key,def);
+    }
+
+}
diff --git a/src/com/android/soundrecorder/Recorder.java b/src/com/android/soundrecorder/Recorder.java
old mode 100644
new mode 100755
index b9654cd..989e8eb
--- a/src/com/android/soundrecorder/Recorder.java
+++ b/src/com/android/soundrecorder/Recorder.java
@@ -1,4 +1,7 @@
 /*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
  * Copyright (C) 2011 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,65 +21,104 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.text.SimpleDateFormat;
 
 import android.content.Context;
 import android.media.AudioManager;
-import android.media.MediaPlayer;
 import android.media.MediaRecorder;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.media.MediaPlayer.OnErrorListener;
+import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
+import android.text.TextUtils;
 import android.util.Log;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.os.Handler;
+import android.os.Message;
 
-public class Recorder implements OnCompletionListener, OnErrorListener {
+import com.android.soundrecorder.util.FileUtils;
+import com.android.soundrecorder.util.StorageUtils;
+
+public class Recorder implements MediaRecorder.OnInfoListener {
+    static final String TAG = "Recorder";
     static final String SAMPLE_PREFIX = "recording";
     static final String SAMPLE_PATH_KEY = "sample_path";
     static final String SAMPLE_LENGTH_KEY = "sample_length";
 
     public static final int IDLE_STATE = 0;
     public static final int RECORDING_STATE = 1;
-    public static final int PLAYING_STATE = 2;
-    
+    public static final int PAUSE_STATE = 2;
+
     int mState = IDLE_STATE;
 
     public static final int NO_ERROR = 0;
     public static final int SDCARD_ACCESS_ERROR = 1;
     public static final int INTERNAL_ERROR = 2;
     public static final int IN_CALL_RECORD_ERROR = 3;
-    
+    public static final int UNSUPPORTED_FORMAT = 4;
+    public static final int RECORD_INTERRUPTED = 5;
+    public static final int RECORD_LOST_FOCUS = 6;
+
+    static final int FOCUSCHANGE = 0;
+
+    public int mChannels = 0;
+    public int mSamplingRate = 0;
+    private int mBitRate = 0;
+
+    public String mStoragePath = null;
+
+    private int mMaxDuration;
+
     public interface OnStateChangedListener {
         public void onStateChanged(int state);
         public void onError(int error);
+        public void onInfo(int what, int extra);
     }
     OnStateChangedListener mOnStateChangedListener = null;
-    
+
+    MediaRecorder.OnErrorListener mMRErrorListener = new MediaRecorder.OnErrorListener() {
+        public void onError(MediaRecorder mr, int what, int extra) {
+            stop();
+            setError(RECORD_INTERRUPTED);
+        }
+    };
+
     long mSampleStart = 0;       // time at which latest record or play operation started
-    int mSampleLength = 0;      // length of current sample
+    long mSampleLength = 0;      // length of current sample
     File mSampleFile = null;
-    
+
     MediaRecorder mRecorder = null;
-    MediaPlayer mPlayer = null;
-    
+    private AudioManager mAudioManager;
+    Context mContext = null;
+
+    public Recorder(Context context) {
+        if (context.getResources().getBoolean(R.bool.config_storage_path)) {
+            mStoragePath = StorageUtils.applyCustomStoragePath(context);
+        } else {
+            mStoragePath = StorageUtils.getPhoneStoragePath();
+        }
+        mContext = context;
+        mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+    }
+
     public Recorder() {
     }
-    
+
     public void saveState(Bundle recorderState) {
         recorderState.putString(SAMPLE_PATH_KEY, mSampleFile.getAbsolutePath());
-        recorderState.putInt(SAMPLE_LENGTH_KEY, mSampleLength);
+        recorderState.putLong(SAMPLE_LENGTH_KEY, mSampleLength);
     }
-    
+
     public int getMaxAmplitude() {
         if (mState != RECORDING_STATE)
             return 0;
         return mRecorder.getMaxAmplitude();
     }
-    
+
     public void restoreState(Bundle recorderState) {
         String samplePath = recorderState.getString(SAMPLE_PATH_KEY);
         if (samplePath == null)
             return;
-        int sampleLength = recorderState.getInt(SAMPLE_LENGTH_KEY, -1);
+        long sampleLength = recorderState.getLong(SAMPLE_LENGTH_KEY, -1);
         if (sampleLength == -1)
             return;
 
@@ -86,96 +128,180 @@
         if (mSampleFile != null
                 && mSampleFile.getAbsolutePath().compareTo(file.getAbsolutePath()) == 0)
             return;
-        
+
         delete();
         mSampleFile = file;
         mSampleLength = sampleLength;
 
         signalStateChanged(IDLE_STATE);
     }
-    
+
     public void setOnStateChangedListener(OnStateChangedListener listener) {
         mOnStateChangedListener = listener;
     }
-    
+
+    public void setChannels(int nChannelsCount) {
+        mChannels = nChannelsCount;
+    }
+
+    public void setSamplingRate(int samplingRate) {
+        mSamplingRate = samplingRate;
+    }
+
+    public void setAudioEncodingBitRate(int bitRate) {
+        mBitRate = bitRate;
+    }
+
     public int state() {
         return mState;
     }
-    
+
     public int progress() {
-        if (mState == RECORDING_STATE || mState == PLAYING_STATE)
-            return (int) ((System.currentTimeMillis() - mSampleStart)/1000);
+        if (mState == RECORDING_STATE) {
+            return (int) ((mSampleLength + (System.currentTimeMillis() - mSampleStart)) / 1000);
+        }
         return 0;
     }
-    
+
     public int sampleLength() {
+        return (int) (mSampleLength / 1000);
+    }
+
+    public long sampleLengthMillis() {
         return mSampleLength;
     }
 
     public File sampleFile() {
         return mSampleFile;
     }
-    
+
+    public void renameSampleFile(String newName) {
+        mSampleFile = FileUtils.renameFile(mSampleFile, newName);
+    }
+
     /**
      * Resets the recorder state. If a sample was recorded, the file is deleted.
      */
     public void delete() {
         stop();
-        
-        if (mSampleFile != null)
+
+        if (mSampleFile != null){
             mSampleFile.delete();
+        }
 
         mSampleFile = null;
         mSampleLength = 0;
-        
+
         signalStateChanged(IDLE_STATE);
     }
-    
+
     /**
-     * Resets the recorder state. If a sample was recorded, the file is left on disk and will 
+     * Resets the recorder state. If a sample was recorded, the file is left on disk and will
      * be reused for a new recording.
      */
     public void clear() {
         stop();
-        
+
+        mSampleFile = null;
         mSampleLength = 0;
-        
+
         signalStateChanged(IDLE_STATE);
     }
-    
-    public void startRecording(int outputfileformat, String extension, Context context) {
-        stop();
-        
-        if (mSampleFile == null) {
-            File sampleDir = Environment.getExternalStorageDirectory();
-            if (!sampleDir.canWrite()) // Workaround for broken sdcard support on the device.
-                sampleDir = new File("/sdcard/sdcard");
-            
-            try {
-                mSampleFile = File.createTempFile(SAMPLE_PREFIX, extension, sampleDir);
-            } catch (IOException e) {
-                setError(SDCARD_ACCESS_ERROR);
-                return;
-            }
+
+    /**
+     * Resets the recorder state before prepared. If a sample was recorded, the file is deleted.
+     */
+    public void release(int err) {
+        releaseRecording(err);
+
+        if (mSampleFile != null){
+            mSampleFile.delete();
         }
-        
+        mSampleFile = null;
+        mSampleLength = 0;
+
+        signalStateChanged(IDLE_STATE);
+    }
+
+    public void startRecording(int outputfileformat, String extension,
+                   Context context, int audiosourcetype, int codectype) {
+        stop();
+
+        if (mSampleFile != null) {
+            mSampleFile.delete();
+            mSampleFile = null;
+            mSampleLength = 0;
+        }
+
+        File sampleDir = new File(mStoragePath);
+
+        if (!sampleDir.exists()) {
+            sampleDir.mkdirs();
+        }
+
+        if (!sampleDir.canWrite()) // Workaround for broken sdcard support on the device.
+            sampleDir = new File(StorageUtils.getSdStoragePath(context));
+
+        try {
+            String prefix = context.getResources().getString(R.string.def_save_name_prefix);
+            if (!"".equals(prefix)) {
+                //long index = FileUtils.getSuitableIndexOfRecording(prefix);
+                //mSampleFile = createTempFile(prefix, Long.toString(index), extension, sampleDir);
+                mSampleFile = createTempFile(context, prefix+"-", extension, sampleDir);
+            } else {
+                prefix = SAMPLE_PREFIX + '-';
+                mSampleFile = createTempFile(context, prefix, extension, sampleDir);
+            }
+        } catch (IOException e) {
+            setError(SDCARD_ACCESS_ERROR);
+            return;
+        }
+
+
         mRecorder = new MediaRecorder();
-        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
-        mRecorder.setOutputFormat(outputfileformat);
-        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
-        mRecorder.setOutputFile(mSampleFile.getAbsolutePath());
+
+        try {
+            mRecorder.setAudioSource(audiosourcetype);
+        } catch(RuntimeException exception) {
+            release(INTERNAL_ERROR);
+            return;
+        }
+
+        //set channel for surround sound recording.
+        if (mChannels > 0) {
+            mRecorder.setAudioChannels(mChannels);
+        }
+        if (mSamplingRate > 0) {
+            mRecorder.setAudioSamplingRate(mSamplingRate);
+        }
+        if (mBitRate > 0) {
+            mRecorder.setAudioEncodingBitRate(mBitRate);
+        }
+
+        try {
+            mRecorder.setOutputFormat(outputfileformat);
+            mRecorder.setOnErrorListener(mMRErrorListener);
+
+            mRecorder.setMaxDuration(mMaxDuration);
+            mRecorder.setOnInfoListener(this);
+
+            mRecorder.setAudioEncoder(codectype);
+        } catch(RuntimeException exception) {
+            release(UNSUPPORTED_FORMAT);
+            return;
+        }
 
         // Handle IOException
         try {
+            mRecorder.setOutputFile(mSampleFile.getAbsolutePath());
+
             mRecorder.prepare();
-        } catch(IOException exception) {
-            setError(INTERNAL_ERROR);
-            mRecorder.reset();
-            mRecorder.release();
-            mRecorder = null;
+        } catch(IOException | IllegalStateException exception) {
+            release(INTERNAL_ERROR);
             return;
         }
         // Handle RuntimeException if the recording couldn't start
+        Log.d(TAG,"audiosourcetype " +audiosourcetype);
         try {
             mRecorder.start();
         } catch (RuntimeException exception) {
@@ -187,91 +313,205 @@
             } else {
                 setError(INTERNAL_ERROR);
             }
-            mRecorder.reset();
-            mRecorder.release();
-            mRecorder = null;
+            delete();
             return;
         }
         mSampleStart = System.currentTimeMillis();
         setState(RECORDING_STATE);
+        stopAudioPlayback();
     }
-    
-    public void stopRecording() {
-        if (mRecorder == null)
-            return;
 
-        mRecorder.stop();
+    public void pauseRecording() {
+        if (mRecorder == null) {
+            return;
+        }
+        try {
+            mRecorder.pause();
+        } catch (RuntimeException exception) {
+            setError(INTERNAL_ERROR);
+            Log.e(TAG, "Pause Failed");
+        }
+        mSampleLength = mSampleLength + (System.currentTimeMillis() - mSampleStart);
+        setState(PAUSE_STATE);
+    }
+
+    public void resumeRecording() {
+        if (mRecorder == null) {
+            return;
+        }
+        stopAudioPlayback();
+        try {
+            if(Build.VERSION.SDK_INT >= 23){
+                mRecorder.resume();
+            }else{
+                mRecorder.start();
+            }
+        } catch (RuntimeException exception) {
+            setError(INTERNAL_ERROR);
+            Log.e(TAG, "Resume Failed");
+        }
+        mSampleStart = System.currentTimeMillis();
+        setState(RECORDING_STATE);
+    }
+
+    public void stopRecording() {
+        if (mRecorder == null){
+            return;
+        }
+
+        try {
+            if ((PAUSE_STATE == mState) && (Build.VERSION.SDK_INT >= 23)){
+                resumeRecording();
+                setState(RECORDING_STATE);
+            }
+            mRecorder.stop();
+        }catch (RuntimeException exception){
+            if(sampleLength() > 1){
+                setError(INTERNAL_ERROR);
+                Log.e(TAG, "Stop Failed");
+            }else{
+                Log.e(TAG, "Quickly Stop Failed");
+            }
+        }
+
+        mRecorder.reset();
+        mRecorder.release();
+        mRecorder = null;
+        mChannels = 0;
+        mSamplingRate = 0;
+        if (mState == RECORDING_STATE) {
+            mSampleLength = mSampleLength + (System.currentTimeMillis() - mSampleStart);
+        }
+        setState(IDLE_STATE);
+    }
+
+
+    public void releaseRecording(int err) {
+
+        setError(err);
+
+        if (mRecorder == null){
+            return;
+        }
+
+        mRecorder.reset();
         mRecorder.release();
         mRecorder = null;
 
-        mSampleLength = (int)( (System.currentTimeMillis() - mSampleStart)/1000 );
-        setState(IDLE_STATE);
     }
-    
-    public void startPlayback() {
-        stop();
-        
-        mPlayer = new MediaPlayer();
-        try {
-            mPlayer.setDataSource(mSampleFile.getAbsolutePath());
-            mPlayer.setOnCompletionListener(this);
-            mPlayer.setOnErrorListener(this);
-            mPlayer.prepare();
-            mPlayer.start();
-        } catch (IllegalArgumentException e) {
-            setError(INTERNAL_ERROR);
-            mPlayer = null;
-            return;
-        } catch (IOException e) {
-            setError(SDCARD_ACCESS_ERROR);
-            mPlayer = null;
-            return;
-        }
-        
-        mSampleStart = System.currentTimeMillis();
-        setState(PLAYING_STATE);
-    }
-    
-    public void stopPlayback() {
-        if (mPlayer == null) // we were not in playback
-            return;
 
-        mPlayer.stop();
-        mPlayer.release();
-        mPlayer = null;
-        setState(IDLE_STATE);
-    }
-    
     public void stop() {
         stopRecording();
-        stopPlayback();
+        mAudioManager.abandonAudioFocus(mAudioFocusListener);
     }
 
-    public boolean onError(MediaPlayer mp, int what, int extra) {
-        stop();
-        setError(SDCARD_ACCESS_ERROR);
-        return true;
-    }
-
-    public void onCompletion(MediaPlayer mp) {
-        stop();
-    }
-    
     private void setState(int state) {
         if (state == mState)
             return;
-        
+
         mState = state;
         signalStateChanged(mState);
     }
-    
+
     private void signalStateChanged(int state) {
         if (mOnStateChangedListener != null)
             mOnStateChangedListener.onStateChanged(state);
     }
-    
+
     private void setError(int error) {
         if (mOnStateChangedListener != null)
             mOnStateChangedListener.onError(error);
     }
+
+    public void setStoragePath(String path) {
+        mStoragePath = path;
+    }
+
+    public File createTempFile(String prefix, String fileName, String suffix, File directory)
+            throws IOException {
+        // Force a prefix null check first
+        if (prefix.length() < 3) {
+            throw new IllegalArgumentException("prefix must be at least 3 characters");
+        }
+        if (suffix == null) {
+            suffix = ".tmp";
+        }
+        File tmpDirFile = directory;
+        if (tmpDirFile == null) {
+            String tmpDir = System.getProperty("java.io.tmpdir", ".");
+            tmpDirFile = new File(tmpDir);
+        }
+
+        File result;
+        do {
+            result = new File(tmpDirFile, prefix + fileName + suffix);
+        } while (!result.createNewFile());
+        return result;
+    }
+
+    public File createTempFile(Context context, String prefix, String suffix, File directory)
+            throws IOException {
+        String nameFormat = context.getResources().getString(R.string.def_save_name_format);
+        SimpleDateFormat df = new SimpleDateFormat(nameFormat);
+        String currentTime = df.format(System.currentTimeMillis());
+        if (!TextUtils.isEmpty(currentTime)) {
+            currentTime = currentTime.replaceAll("[\\\\*|\":<>/?]", "_").replaceAll(" ",
+                    "\\\\" + " ");
+        }
+        return createTempFile(prefix, currentTime, suffix, directory);
+    }
+
+    public void setMaxDuration(int duration) {
+        mMaxDuration = duration;
+    }
+
+    @Override
+    public void onInfo(MediaRecorder mr, int what, int extra) {
+        if (mOnStateChangedListener != null) {
+            mOnStateChangedListener.onInfo(what, extra);
+        }
+    }
+
+    /*
+     * Make sure we're not recording music playing in the background, ask
+     * the MediaPlaybackService to pause playback.
+     */
+    private void stopAudioPlayback() {
+        AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+        am.requestAudioFocus(mAudioFocusListener,
+                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+    }
+
+    private OnAudioFocusChangeListener mAudioFocusListener =
+        new OnAudioFocusChangeListener() {
+            public void onAudioFocusChange(int focusChange) {
+                mRecorderHandler.obtainMessage(FOCUSCHANGE, focusChange, 0)
+                    .sendToTarget();
+        }
+    };
+
+    private Handler mRecorderHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case FOCUSCHANGE:
+                    switch (msg.arg1) {
+                        case AudioManager.AUDIOFOCUS_LOSS:
+                            if (state() == Recorder.RECORDING_STATE) {
+                                stop();
+                                setError(RECORD_LOST_FOCUS);
+                            }
+                            break;
+
+                        default:
+                            break;
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+    };
+
 }
diff --git a/src/com/android/soundrecorder/RenameDialogBuilder.java b/src/com/android/soundrecorder/RenameDialogBuilder.java
new file mode 100644
index 0000000..328fa15
--- /dev/null
+++ b/src/com/android/soundrecorder/RenameDialogBuilder.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.soundrecorder.util.FileUtils;
+
+import java.io.File;
+
+public class RenameDialogBuilder extends AlertDialog.Builder {
+    public interface OnPositiveListener {
+        public void onClick(DialogInterface dialog, int which, String extra);
+    }
+
+    private static final String EMPTY_EXTENSION = "";
+    private static final int FILENAME_CHAR_LIMIT = 255;
+    private String mFolderPath;
+    private String mFileName;
+    private String mFileExtension;
+    private View mCustomLayout;
+    private EditText mEditText;
+    private TextView mInfoText;
+    private View mInfoLayout;
+
+
+    public RenameDialogBuilder(Context context, File origFile) {
+        super(context);
+        if (origFile != null) {
+            mFolderPath = origFile.getParent();
+            mFileName = origFile.getName(); // with extension
+            mFileExtension = FileUtils.getFileExtension(origFile, true);
+        }
+        if (mFileExtension == null) {
+            mFileExtension = EMPTY_EXTENSION;
+        }
+        initCustomLayout();
+    }
+
+    @Override
+    public AlertDialog create() {
+        AlertDialog dialog = super.create();
+
+        dialog.setCanceledOnTouchOutside(false);
+
+        dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+        dialog.setView(mCustomLayout);
+
+        return dialog;
+    }
+
+    @Override
+    public AlertDialog show() {
+        final AlertDialog dialog = super.show();
+
+        Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
+        if (negativeButton != null) {
+            negativeButton.setTextColor(getContext().getColor(R.color.negative_button_color));
+        }
+
+        if (mEditText != null) {
+            mEditText.addTextChangedListener(new TextWatcher() {
+                @Override
+                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                }
+
+                @Override
+                public void onTextChanged(CharSequence s, int start, int before, int count) {
+                }
+
+                @Override
+                public void afterTextChanged(Editable s) {
+                    checkFileName(dialog, s);
+                }
+            });
+            mEditText.requestFocusFromTouch();
+        }
+        return dialog;
+    }
+
+    /** check file name is valid or not. */
+    private void checkFileName(AlertDialog dialog, Editable editable) {
+        boolean errorEmptyLength = (editable.length() == 0);
+        boolean errorFileExists = false;
+
+        int fileExtensionLength = mFileExtension == null ? 0 : mFileExtension.length();
+        boolean errorFileNameLengthLimit =
+                (editable.length() + fileExtensionLength) >= FILENAME_CHAR_LIMIT;
+
+        if (mFolderPath != null) {
+            File newFile = new File(mFolderPath + File.separator + editable.toString()
+                    + mFileExtension);
+            // if input name is same as original name, ignore it.
+            if (mFileName != null && !mFileName.equals(editable.toString() + mFileExtension)) {
+                errorFileExists = FileUtils.exists(newFile);
+            }
+        }
+
+        if (errorEmptyLength || errorFileExists || errorFileNameLengthLimit) {
+            dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+            mInfoLayout.setVisibility(View.VISIBLE);
+            if (errorEmptyLength) {
+                mInfoText.setText(R.string.rename_dialog_info_null);
+            }
+            if (errorFileNameLengthLimit) {
+                mInfoText.setText(R.string.rename_dialog_info_length_limit);
+            }
+            if (errorFileExists) {
+                mInfoText.setText(R.string.rename_dialog_info_exists);
+            }
+        } else {
+            dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+            mInfoLayout.setVisibility(View.GONE);
+        }
+    }
+
+    private void initCustomLayout() {
+        mCustomLayout = View.inflate(getContext(), R.layout.rename_dialog, null);
+        mEditText = (EditText) mCustomLayout.findViewById(R.id.rename_edit_text);
+        mInfoText = (TextView) mCustomLayout.findViewById(R.id.rename_info_text);
+        mInfoLayout = mCustomLayout.findViewById(R.id.rename_info_layout);
+    }
+
+    public void setEditTextContent(CharSequence content) {
+        if (mEditText != null) {
+            mEditText.setText(content);
+            mEditText.setSelection(mEditText.getText().length());
+        }
+    }
+
+    public RenameDialogBuilder setPositiveButton(int textId,
+            final OnPositiveListener onPositiveListener) {
+        this.setPositiveButton(textId, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                onClickPositiveButton(onPositiveListener, dialog, which);
+            }
+        });
+        return this;
+    }
+
+    public RenameDialogBuilder setPositiveButton(CharSequence text,
+            final OnPositiveListener onPositiveListener) {
+        this.setPositiveButton(text, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                onClickPositiveButton(onPositiveListener, dialog, which);
+            }
+        });
+        return this;
+    }
+
+    private void onClickPositiveButton(final OnPositiveListener onPositiveListener,
+            DialogInterface dialog, int which) {
+        if (onPositiveListener != null) {
+            onPositiveListener.onClick(dialog, which,
+                    mEditText == null ? null : mEditText.getText().toString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/soundrecorder/SoundRecorder.java b/src/com/android/soundrecorder/SoundRecorder.java
old mode 100644
new mode 100755
index 3970188..bf360e0
--- a/src/com/android/soundrecorder/SoundRecorder.java
+++ b/src/com/android/soundrecorder/SoundRecorder.java
@@ -1,4 +1,7 @@
 /*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
  * Copyright (C) 2011 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,44 +20,64 @@
 package com.android.soundrecorder;
 
 import java.io.File;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.util.Hashtable;
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.content.ContentResolver;
-import android.content.ContentValues;
+import android.app.ProgressDialog;
 import android.content.Intent;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.IntentFilter;
 import android.content.BroadcastReceiver;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.database.Cursor;
 import android.media.AudioManager;
-import android.media.MediaRecorder;
 import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
 import android.os.PowerManager;
-import android.os.StatFs;
 import android.os.PowerManager.WakeLock;
 import android.provider.MediaStore;
 import android.util.Log;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
 import android.widget.TextView;
+import android.widget.Toast;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
+
+import com.android.soundrecorder.util.DatabaseUtils;
+import com.android.soundrecorder.filelist.FileListActivity;
+import com.android.soundrecorder.util.FileUtils;
+import com.android.soundrecorder.util.PermissionUtils;
+import com.android.soundrecorder.util.StorageUtils;
+import com.android.soundrecorder.util.Utils;
+import org.codeaurora.wrapper.soundrecorder.util.SystemPropertiesWrapper;
+import org.codeaurora.wrapper.soundrecorder.util.MediaRecorderWrapper;
+
 
 /**
  * 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.
  */
@@ -63,47 +86,47 @@
     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;
-    
+
      // State for tracking file size of recording.
     private File mRecordingFile;
     private long mMaxBytes;
-    
+
     // Rate at which the file grows
     private int mBytesPerSecond;
-    
+    private int mPath = StorageUtils.STORAGE_PATH_PHONE_INDEX;
+
     // time at which number of free blocks last changed
     private long mBlocksChangedTime;
     // number of available blocks at that time
-    private long mLastBlocks;
-    
+    private long mLastBlockSize;
+
     // 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();
-    }    
-    
+    private Context mContext;
+
+    public RemainingTimeCalculator(Context context) {
+        mContext = context;
+    }
+
     /**
      * 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.
      */
@@ -112,41 +135,42 @@
         mBlocksChangedTime = -1;
         mFileSizeChangedTime = -1;
     }
-    
+
     /**
-     * Returns how long (in seconds) we can continue recording. 
+     * Returns how long (in seconds) we can continue recording.
      */
     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 blockSize = StorageUtils.getAvailableBlockSize(mContext, mPath);
         long now = System.currentTimeMillis();
-        
-        if (mBlocksChangedTime == -1 || blocks != mLastBlocks) {
+
+        if (mBlocksChangedTime == -1 || blockSize != mLastBlockSize) {
             mBlocksChangedTime = now;
-            mLastBlocks = blocks;
+            mLastBlockSize = blockSize;
         }
 
         /* 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 
+           last block might get nibbled when we close and flush the file, but
            we won't run out of disk. */
-        
+
         // at mBlocksChangedTime we had this much time
-        long result = mLastBlocks*blockSize/mBytesPerSecond;
+        if (mBytesPerSecond == 0) {
+            mBytesPerSecond = 1;
+        }
+        long result = mLastBlockSize / mBytesPerSecond;
         // so now we have this much time
         result -= (now - mBlocksChangedTime)/1000;
-        
+
         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) {
@@ -157,16 +181,16 @@
         long result2 = (mMaxBytes - fileSize)/mBytesPerSecond;
         result2 -= (now - mFileSizeChangedTime)/1000;
         result2 -= 1; // just for safety
-        
+
         mCurrentLowerLimit = result < result2
             ? DISK_SPACE_LIMIT : FILE_SIZE_LIMIT;
-        
+
         return Math.min(result, result2);
     }
-    
+
     /**
-     * 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 
+     * 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() {
@@ -174,15 +198,6 @@
     }
 
     /**
-     * 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.
@@ -190,98 +205,246 @@
     public void setBitRate(int bitRate) {
         mBytesPerSecond = bitRate/8;
     }
+
+    public void setStoragePath(int path) {
+        mPath = path;
+    }
 }
 
-public class SoundRecorder extends Activity 
+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 RECORDER_STATE_KEY = "recorder_state";
     static final String SAMPLE_INTERRUPTED_KEY = "sample_interrupted";
     static final String MAX_FILE_SIZE_KEY = "max_file_size";
+    private static final String EXIT_AFTER_RECORD = "exit_after_record";
 
     static final String AUDIO_3GPP = "audio/3gpp";
     static final String AUDIO_AMR = "audio/amr";
+    static final String AUDIO_EVRC = "audio/evrc";
+    static final String AUDIO_QCELP = "audio/qcelp";
+    static final String AUDIO_AAC_MP4 = "audio/aac_mp4";
+    static final String AUDIO_WAVE_6CH_LPCM = "audio/wave_6ch_lpcm";
+    static final String AUDIO_WAVE_2CH_LPCM = "audio/wave_2ch_lpcm";
+    static final String AUDIO_AAC_5POINT1_CHANNEL = "audio/aac_5point1_channel";
+    static final String AUDIO_AMR_WB = "audio/amr-wb";
     static final String AUDIO_ANY = "audio/*";
     static final String ANY_ANY = "*/*";
-    
-    static final int BITRATE_AMR =  5900; // bits/sec
-    static final int BITRATE_3GPP = 5900;
-    
+
+    static final int FOCUSCHANGE = 0;
+
+    static final String VENDOR_SOUNDRECORDER_DEBUG_ENABLE = "vendor.soundrecorder.debug.enable";
+
+    static final int SETTING_TYPE_STORAGE_LOCATION = 0;
+    static final int SETTING_TYPE_FILE_TYPE = 1;
+
+    static final int BITRATE_AMR = 12800; // bits/sec
+    static final int BITRATE_AAC = 156000;
+    static final int BITRATE_EVRC = 8500;
+    static final int BITRATE_LPCM = 4608000;
+    static final int BITRATE_QCELP = 13300;
+    static final int BITRATE_3GPP = 12800;
+    static final int SAMPLERATE_MULTI_CH = 48000;
+    static final int BITRATE_AMR_WB = 23850;
+    static final int SAMPLERATE_AMR_WB = 16000;
+    static final int SAMPLERATE_8000 = 8000;
+    static final long STOP_WAIT = 300;
+    static final long BACK_KEY_WAIT = 400;
+    int mAudioOutputFormat = MediaRecorderWrapper.OutputFormat.AMR_WB;
+    String mAmrWidebandExtension = ".awb";
+    private AudioManager mAudioManager;
+    private boolean mRecorderStop = false;
+    private boolean mRecorderProcessed = false;
+    private boolean mDataExist = false;
+    private boolean mWAVSupport = true;
+    private boolean mExitAfterRecord = false;
+    private boolean mIsGetContentAction = false;
+    private boolean mSdExist = true;
+    private boolean mRenameDialogShown = false;
+
+    private ProgressDialog mProgressDialog;
+    private final int MSG_DISMISS_PROGRESS_DIALOG = 1100;
+
+    int mAudioSourceType = MediaRecorderWrapper.AudioSource.MIC;
+    int mPhoneCount = 0;
+    private Hashtable<Integer, Integer> mCallStateMap = new Hashtable<Integer, Integer>();
+    static int mCallState = TelephonyManager.CALL_STATE_IDLE;
     WakeLock mWakeLock;
     String mRequestedType = AUDIO_ANY;
     Recorder mRecorder;
-    boolean mSampleInterrupted = false;    
-    String mErrorUiMessage = null; // Some error messages are displayed in the UI, 
+    boolean mSampleInterrupted = false;
+    static boolean bSSRSupported;
+    String mErrorUiMessage = null; // Some error messages are displayed in the UI,
                                    // not a dialog. This happens when a recording
                                    // is interrupted for some reason.
-    
+
     long mMaxFileSize = -1;        // can be specified in the intent
-    RemainingTimeCalculator mRemainingTimeCalculator;
-    
+    RemainingTimeCalculator mRemainingTimeCalculator = null;
+
     String mTimerFormat;
     final Handler mHandler = new Handler();
     Runnable mUpdateTimer = new Runnable() {
-        public void run() { updateTimerView(); }
+        public void run() {
+            updateTimerView();
+        }
     };
 
     ImageButton mRecordButton;
-    ImageButton mPlayButton;
     ImageButton mStopButton;
-    
-    ImageView mStateLED;
+    ImageButton mListButton;
+
     TextView mStateMessage1;
     TextView mStateMessage2;
-    ProgressBar mStateProgressBar;
     TextView mTimerView;
-    
-    LinearLayout mExitButtons;
-    Button mAcceptButton;
-    Button mDiscardButton;
+
     VUMeter mVUMeter;
     private BroadcastReceiver mSDCardMountEventReceiver = null;
+    private BroadcastReceiver mPowerOffReceiver = null;
+    private TelephonyManager mTelephonyManager;
+    private PhoneStateListener[] mPhoneStateListener;
+    private int mFileType = 0;
+    private int mPath = StorageUtils.STORAGE_PATH_PHONE_INDEX;
+    private String mStoragePath = StorageUtils.getPhoneStoragePath();
+    private SharedPreferences mSharedPreferences;
+    private Editor mPrefsStoragePathEditor;
+
+    private IntentFilter mMountFilter = new IntentFilter();
+
+    private PhoneStateListener getPhoneStateListener(int subId) {
+        PhoneStateListener phoneStateListener = new PhoneStateListener(subId) {
+            @Override
+            public void onCallStateChanged(int state, String ignored) {
+               mCallStateMap.put(this.mSubId, state);
+
+               switch (state) {
+                      case TelephonyManager.CALL_STATE_IDLE:
+                      if ((mCallState == TelephonyManager.CALL_STATE_OFFHOOK)
+                               && !(mAudioSourceType == MediaRecorderWrapper.AudioSource.MIC)){
+                         mRecorder.stop();
+                          // TODO show toast here.
+                         mAudioSourceType = MediaRecorderWrapper.AudioSource.MIC;
+                      }
+
+                      case TelephonyManager.CALL_STATE_RINGING:
+                      case TelephonyManager.CALL_STATE_OFFHOOK:
+
+                      if (mCallStateMap.containsValue(TelephonyManager.CALL_STATE_OFFHOOK)) {
+                          mCallState = TelephonyManager.CALL_STATE_OFFHOOK;
+                      } else if(mCallStateMap.containsValue(TelephonyManager.CALL_STATE_RINGING)) {
+                          mCallState = TelephonyManager.CALL_STATE_RINGING;
+                      } else {
+                          mCallState = TelephonyManager.CALL_STATE_IDLE;
+                      }
+
+                      break;
+
+                      default:
+                      // The control should not come here
+                      Log.e(TAG,"Unknown call state");
+                      break;
+                }
+            }
+        };
+        return phoneStateListener;
+    }
+
+    private BroadcastReceiver mMountReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (mPath == StorageUtils.STORAGE_PATH_PHONE_INDEX
+                    && StorageUtils.isPhoneStorageMounted()) {
+                mErrorUiMessage = null;
+                updateUi();
+            } else if (mPath == StorageUtils.STORAGE_PATH_SD_INDEX
+                    && StorageUtils.isSdMounted(SoundRecorder.this)) {
+                mErrorUiMessage = null;
+                mSdExist = true;
+                updateUi();
+            } else if (StorageUtils.isSdMounted(SoundRecorder.this) &&
+                    !StorageUtils.diskSpaceAvailable(SoundRecorder.this, mPath)) {
+                mSampleInterrupted = true;
+                mErrorUiMessage = getResources().getString(R.string.storage_is_full);
+                updateUi();
+            }
+        }
+    };
 
     @Override
     public void onCreate(Bundle icycle) {
         super.onCreate(icycle);
+        if (getResources().getBoolean(R.bool.config_storage_path)) {
+            mStoragePath = StorageUtils.applyCustomStoragePath(this);
+        }
 
+        mSharedPreferences = getSharedPreferences("storage_Path", Context.MODE_PRIVATE);
+        mPrefsStoragePathEditor = mSharedPreferences.edit();
+
+        int maxDuration = 0;
         Intent i = getIntent();
         if (i != null) {
             String s = i.getType();
             if (AUDIO_AMR.equals(s) || AUDIO_3GPP.equals(s) || AUDIO_ANY.equals(s)
                     || ANY_ANY.equals(s)) {
                 mRequestedType = s;
+                mWAVSupport = false;
             } else if (s != null) {
-                // we only support amr and 3gpp formats right now 
+                // 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);
+
+            mIsGetContentAction = Intent.ACTION_GET_CONTENT.equals(i.getAction());
+
+            mExitAfterRecord = i.getBooleanExtra(EXIT_AFTER_RECORD, mIsGetContentAction);
+            maxDuration = i.getIntExtra(MediaStore.Audio.Media.DURATION, 0);
         }
-        
+
         if (AUDIO_ANY.equals(mRequestedType) || ANY_ANY.equals(mRequestedType)) {
-            mRequestedType = AUDIO_3GPP;
+            mRequestedType = AUDIO_AMR;
         }
-        
+
+        mPath = mSharedPreferences.getInt("path", mPath);
+        if (!mExitAfterRecord) {
+            // Don't reload cached encoding type,if it's assigned by external intent.
+            mRequestedType = mSharedPreferences.getString("requestedType",
+                    getResources().getString(R.string.def_save_mimetype));
+        }
+        mFileType = mSharedPreferences.getInt("fileType",
+                getResources().getInteger(R.integer.def_save_type));
+        mStoragePath = mSharedPreferences.getString("storagePath", mStoragePath);
+        if (!mWAVSupport && (AUDIO_WAVE_2CH_LPCM.equals(mRequestedType))) {
+            mRequestedType = AUDIO_AMR;
+            mFileType = 0;
+        }
+
         setContentView(R.layout.main);
-
-        mRecorder = new Recorder();
+        mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
+        mRecorder = new Recorder(this);
         mRecorder.setOnStateChangedListener(this);
-        mRemainingTimeCalculator = new RemainingTimeCalculator();
+        mRecorder.setMaxDuration(maxDuration);
 
-        PowerManager pm 
+        mRemainingTimeCalculator = new RemainingTimeCalculator(this);
+
+        PowerManager pm
             = (PowerManager) getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, 
+        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
                                     "SoundRecorder");
 
         initResourceRefs();
-        
+
+        mRecorderStop = false;
+        mRecorderProcessed = false;
+        mDataExist = false;
+
         setResult(RESULT_CANCELED);
         registerExternalStorageListener();
+        registerPowerOffListener();
         if (icycle != null) {
             Bundle recorderState = icycle.getBundle(RECORDER_STATE_KEY);
             if (recorderState != null) {
@@ -290,10 +453,50 @@
                 mMaxFileSize = recorderState.getLong(MAX_FILE_SIZE_KEY, -1);
             }
         }
-        
+        mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+        mPhoneCount = mTelephonyManager.getPhoneCount();
+        mPhoneStateListener = new PhoneStateListener[mPhoneCount];
+        for(int j = 0; j < mPhoneCount; j++) {
+            int[] subId = SubscriptionManager.getSubId(j);
+
+            // adapt case: disabled telephony feature or activate card failure
+            if (null != subId && subId.length > 0) {
+                mPhoneStateListener[j] = getPhoneStateListener(subId[0]);
+            } else {
+                mPhoneStateListener[j] = null;
+            }
+        }
+
+        String ssrRet = SystemPropertiesWrapper.get("ro.vendor.qc.sdk.audio.ssr","false");
+        if (ssrRet.contains("true")) {
+            Log.d(TAG,"Surround sound recording is supported");
+            bSSRSupported = true;
+        } else {
+            Log.d(TAG,"Surround sound recording is not supported");
+            bSSRSupported = false;
+        }
+
+        mMountFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
+        mMountFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
+        mMountFilter.addDataScheme("file");
+        registerReceiver(mMountReceiver, mMountFilter);
         updateUi();
     }
-    
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        // While we're in the foreground, listen for phone state changes.
+        mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+        for(int i = 0; i < mPhoneCount; i++) {
+            // adapt case: disabled telephony feature or activate card failure
+            if (null != mPhoneStateListener[i]) {
+                mTelephonyManager.listen(mPhoneStateListener[i],
+                        PhoneStateListener.LISTEN_CALL_STATE);
+            }
+        }
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -302,61 +505,67 @@
         initResourceRefs();
         updateUi();
     }
-    
+
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        
+
         if (mRecorder.sampleLength() == 0)
             return;
 
         Bundle recorderState = new Bundle();
-        
+
         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);
         mStopButton = (ImageButton) findViewById(R.id.stopButton);
-        
-        mStateLED = (ImageView) findViewById(R.id.stateLED);
+        mListButton = (ImageButton) findViewById(R.id.listButton);
+
         mStateMessage1 = (TextView) findViewById(R.id.stateMessage1);
         mStateMessage2 = (TextView) findViewById(R.id.stateMessage2);
-        mStateProgressBar = (ProgressBar) findViewById(R.id.stateProgressBar);
         mTimerView = (TextView) findViewById(R.id.timerView);
-        
-        mExitButtons = (LinearLayout) findViewById(R.id.exitButtons);
-        mAcceptButton = (Button) findViewById(R.id.acceptButton);
-        mDiscardButton = (Button) findViewById(R.id.discardButton);
+
         mVUMeter = (VUMeter) findViewById(R.id.uvMeter);
-        
+
         mRecordButton.setOnClickListener(this);
-        mPlayButton.setOnClickListener(this);
         mStopButton.setOnClickListener(this);
-        mAcceptButton.setOnClickListener(this);
-        mDiscardButton.setOnClickListener(this);
+        mListButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                startListActivity();
+            }
+        });
 
         mTimerFormat = getResources().getString(R.string.timer_format);
-        
+
         mVUMeter.setRecorder(mRecorder);
     }
-    
-    /*
-     * Make sure we're not recording music playing in the background, ask
-     * the MediaPlaybackService to pause playback.
-     */
-    private void stopAudioPlayback() {
-        AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-        am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+
+    private String[] getOperationPermissionName(int operation) {
+        switch (operation) {
+        case R.id.recordButton:
+            return PermissionUtils.getOperationPermissions(PermissionUtils.PermissionType.RECORD);
+        default:
+            return null;
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+                int[] grantResults) {
+        if (PermissionUtils.checkPermissionResult(permissions, grantResults)) {
+            processClickEvent(requestCode);
+        }
     }
 
     /*
@@ -365,81 +574,621 @@
     public void onClick(View button) {
         if (!button.isEnabled())
             return;
+        if (Build.VERSION.SDK_INT >= 23) {
+            String[] permissions = getOperationPermissionName(button.getId());
+            if (PermissionUtils.checkPermissions(this, permissions, button.getId()))
+                processClickEvent(button.getId());
+        } else {
+            processClickEvent(button.getId());
+        }
+    }
 
-        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 (!mRemainingTimeCalculator.diskSpaceAvailable()) {
-                    mSampleInterrupted = true;
-                    mErrorUiMessage = getResources().getString(R.string.storage_is_full);
-                    updateUi();
-                } else {
-                    stopAudioPlayback();
+    private Handler mMsgHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+            switch (msg.what) {
+                case MSG_DISMISS_PROGRESS_DIALOG:
+                    if (mProgressDialog != null && mProgressDialog.isShowing()) {
+                        mProgressDialog.dismiss();
+                        mProgressDialog = null;
+                    }
+                    break;
+            }
+        }
+    };
 
-                    if (AUDIO_AMR.equals(mRequestedType)) {
-                        mRemainingTimeCalculator.setBitRate(BITRATE_AMR);
-                        mRecorder.startRecording(MediaRecorder.OutputFormat.AMR_NB, ".amr", this);
-                    } else if (AUDIO_3GPP.equals(mRequestedType)) {
-                        mRemainingTimeCalculator.setBitRate(BITRATE_3GPP);
-                        mRecorder.startRecording(MediaRecorder.OutputFormat.THREE_GPP, ".3gpp",
-                                this);
+    private class RecordingParams {
+        int mOutputFileFormat;
+        String mExtName;
+        Context mContext;
+        int mAudioSourceType;
+        int mCodecType;
+
+        public RecordingParams(int outputFileFormat,
+                               String extName,
+                               Context context,
+                               int audioSourceType,
+                               int codecType) {
+            mOutputFileFormat = outputFileFormat;
+            mExtName = extName;
+            mContext = context;
+            mAudioSourceType = audioSourceType;
+            mCodecType = codecType;
+        }
+    }
+
+    private class StartRecordingTask extends AsyncTask<RecordingParams, Integer, String> {
+        @Override
+        protected void onPreExecute() {
+            String title = getString(R.string.title_please_wait);
+            String message = getString(R.string.msg_starting_recording);
+            mProgressDialog = ProgressDialog.show(SoundRecorder.this,
+                    message, title);
+            super.onPreExecute();
+        }
+
+        @Override
+        protected String doInBackground(RecordingParams... params) {
+            RecordingParams param = params[0];
+            mRecorder.startRecording(param.mOutputFileFormat, param.mExtName, param.mContext,
+                    param.mAudioSourceType, param.mCodecType);
+            return null;
+        }
+
+        @Override
+        protected void onCancelled(String s) {
+            super.onCancelled(s);
+            Message msg = Message.obtain();
+            msg.what = MSG_DISMISS_PROGRESS_DIALOG;
+            mMsgHandler.sendMessageDelayed(msg, 600);
+        }
+
+        @Override
+        protected void onPostExecute(String s) {
+            super.onPostExecute(s);
+            Message msg = Message.obtain();
+            msg.what = MSG_DISMISS_PROGRESS_DIALOG;
+            mMsgHandler.sendMessageDelayed(msg, 600);
+        }
+    }
+
+    private void processClickEvent(int viewId) {
+        switch (viewId) {
+        case R.id.recordButton:
+            if (mRecorder.state() == Recorder.PAUSE_STATE) {
+                mRecorder.resumeRecording();
+                updateUi();
+                return;
+            } else if (mRecorder.state() == Recorder.RECORDING_STATE) {
+                mRecorder.pauseRecording();
+                updateUi();
+                return;
+            }
+            mRemainingTimeCalculator.reset();
+            mRemainingTimeCalculator.setStoragePath(mPath);
+            mRecorder.setStoragePath(mStoragePath);
+            if (mPath == StorageUtils.STORAGE_PATH_PHONE_INDEX
+                    && !StorageUtils.isPhoneStorageMounted()) {
+                mSampleInterrupted = true;
+                mErrorUiMessage = getResources().getString(R.string.no_phonestorage);
+                updateUi();
+            } else if (mPath == StorageUtils.STORAGE_PATH_SD_INDEX
+                    && !StorageUtils.isSdMounted(SoundRecorder.this)) {
+                mSampleInterrupted = true;
+                mErrorUiMessage = getResources().getString(R.string.insert_sd_card);
+                updateUi();
+            } else if (!StorageUtils.diskSpaceAvailable(SoundRecorder.this, mPath)) {
+                mSampleInterrupted = true;
+                mErrorUiMessage = getResources().getString(R.string.storage_is_full);
+                updateUi();
+            } else {
+
+                if ((mCallState == TelephonyManager.CALL_STATE_OFFHOOK) &&
+                    (mAudioSourceType == MediaRecorderWrapper.AudioSource.MIC)) {
+                    mAudioSourceType = MediaRecorderWrapper.AudioSource.VOICE_UPLINK;
+                    Log.e(TAG, "Selected Voice Tx only Source: sourcetype" + mAudioSourceType);
+                }
+                if (AUDIO_AMR.equals(mRequestedType)) {
+                    mRemainingTimeCalculator.setBitRate(BITRATE_AMR);
+                    mRecorder.setChannels(1);
+                    mRecorder.setSamplingRate(SAMPLERATE_8000);
+                    new StartRecordingTask().execute(new RecordingParams(
+                            MediaRecorderWrapper.OutputFormat.RAW_AMR, ".amr", this,
+                            mAudioSourceType, MediaRecorderWrapper.AudioEncoder.AMR_NB));
+                } else if (AUDIO_EVRC.equals(mRequestedType)) {
+                    mRemainingTimeCalculator.setBitRate(BITRATE_EVRC);
+                    mRecorder.setChannels(1);
+                    mRecorder.setSamplingRate(SAMPLERATE_8000);
+                    new StartRecordingTask().execute(new RecordingParams(
+                            MediaRecorderWrapper.OutputFormat.QCP, ".qcp", this,
+                            mAudioSourceType, MediaRecorderWrapper.AudioEncoder.EVRC));
+                } else if (AUDIO_QCELP.equals(mRequestedType)) {
+                    mRemainingTimeCalculator.setBitRate(BITRATE_QCELP);
+                    mRecorder.setSamplingRate(SAMPLERATE_8000);
+                    mRecorder.setChannels(1);
+                    new StartRecordingTask().execute(new RecordingParams(
+                            MediaRecorderWrapper.OutputFormat.QCP, ".qcp", this,
+                            mAudioSourceType, MediaRecorderWrapper.AudioEncoder.QCELP));
+                } else if (AUDIO_3GPP.equals(mRequestedType)) {
+                    mRemainingTimeCalculator.setBitRate(BITRATE_3GPP);
+                    new StartRecordingTask().execute(new RecordingParams(
+                            MediaRecorderWrapper.OutputFormat.THREE_GPP, ".3gpp", this,
+                            mAudioSourceType, MediaRecorderWrapper.AudioEncoder.AMR_NB));
+                } else if (AUDIO_AAC_MP4.equals(mRequestedType)) {
+                    setBitRate(BITRATE_AAC);
+                    mRecorder.setSamplingRate(SAMPLERATE_MULTI_CH);
+                    mRecorder.setChannels(2);
+                    new StartRecordingTask().execute(new RecordingParams(
+                            MediaRecorderWrapper.OutputFormat.THREE_GPP, ".aac", this,
+                            mAudioSourceType, MediaRecorderWrapper.AudioEncoder.AAC));
+                } else if (AUDIO_AAC_5POINT1_CHANNEL.equals(mRequestedType)) {
+                    //AAC  2-channel recording
+                    if (true == bSSRSupported) {
+                        mRemainingTimeCalculator.setBitRate(BITRATE_AAC);
+                        mRecorder.setChannels(6);
+                        mRecorder.setSamplingRate(SAMPLERATE_MULTI_CH);
+                        mAudioSourceType = MediaRecorderWrapper.AudioSource.MIC;
+                        new StartRecordingTask().execute(new RecordingParams(
+                                MediaRecorderWrapper.OutputFormat.THREE_GPP, ".3gpp", this,
+                                mAudioSourceType, MediaRecorderWrapper.AudioEncoder.AAC));
                     } else {
-                        throw new IllegalArgumentException("Invalid output file type requested");
+                      throw new IllegalArgumentException("Invalid output file type requested");
                     }
-                    
-                    if (mMaxFileSize != -1) {
-                        mRemainingTimeCalculator.setFileSizeLimit(
-                                mRecorder.sampleFile(), mMaxFileSize);
+                } else if (AUDIO_WAVE_6CH_LPCM.equals(mRequestedType)) {
+                    //WAVE LPCM  6-channel recording
+                    if (true == bSSRSupported) {
+                        mRemainingTimeCalculator.setBitRate(BITRATE_LPCM);
+                        mRecorder.setChannels(6);
+                        mRecorder.setSamplingRate(SAMPLERATE_MULTI_CH);
+                        mAudioSourceType = MediaRecorderWrapper.AudioSource.MIC;
+                        new StartRecordingTask().execute(new RecordingParams(
+                                MediaRecorderWrapper.OutputFormat.WAVE, ".wav", this,
+                                mAudioSourceType, MediaRecorderWrapper.AudioEncoder.LPCM));
+                    } else {
+                      throw new IllegalArgumentException("Invalid output file type requested");
                     }
+                } else if (AUDIO_WAVE_2CH_LPCM.equals(mRequestedType)) {
+                    // WAVE LPCM 2-channel recording
+                    mRemainingTimeCalculator.setBitRate(BITRATE_LPCM);
+                    mRecorder.setChannels(2);
+                    mRecorder.setSamplingRate(SAMPLERATE_MULTI_CH);
+                    new StartRecordingTask().execute(new RecordingParams(
+                            MediaRecorderWrapper.OutputFormat.WAVE,
+                            ".wav", this, mAudioSourceType,
+                            MediaRecorderWrapper.AudioEncoder.LPCM));
+                } else if (AUDIO_AMR_WB.equals(mRequestedType)) {
+                    mRemainingTimeCalculator.setBitRate(BITRATE_AMR_WB);
+                    mRecorder.setSamplingRate(BITRATE_AMR_WB);
+                    new StartRecordingTask().execute(new RecordingParams(
+                            mAudioOutputFormat, mAmrWidebandExtension, this,
+                            mAudioSourceType, MediaRecorderWrapper.AudioEncoder.AMR_WB));
+                } else {
+                    throw new IllegalArgumentException("Invalid output file type requested");
+                }
+
+                if (mMaxFileSize != -1) {
+                    mRemainingTimeCalculator.setFileSizeLimit(
+                            mRecorder.sampleFile(), mMaxFileSize);
+                }
+                mRecorderStop = false;
+                mRecorderProcessed = false;
+            }
+            invalidateOptionsMenu();
+            break;
+        case R.id.stopButton:
+            mRecorder.stop();
+            showRenameDialogIfNeed();
+            mVUMeter.resetAngle();
+            invalidateOptionsMenu();
+            break;
+        }
+    }
+
+    private boolean acceptSample(String newName) {
+        boolean isExists = FileUtils.exists(mRecorder.sampleFile());
+        if (!isExists) {
+            Toast.makeText(SoundRecorder.this, R.string.file_deleted,Toast.LENGTH_SHORT).show();
+        }
+        if (newName != null && !newName.equals(getLastFileName(false))) {
+            mRecorder.renameSampleFile(newName);
+        }
+        mSampleInterrupted = false;
+        mRecorder.stop();
+        mRecorderProcessed = true;
+        if (isExists) {
+            saveSample(false);
+        } else {
+            // reset mRecorder and restore UI.
+            mRecorder.clear();
+            updateUi();
+        }
+        mVUMeter.resetAngle();
+        if (mExitAfterRecord) {
+            finish();
+            return false;
+        }
+        return true;
+    }
+
+    private void discardSample() {
+        mSampleInterrupted = false;
+        mRecorder.delete();
+        mRecorderProcessed = true;
+        mVUMeter.resetAngle();
+    }
+
+    private void setBitRate(int bitRate) {
+        mRemainingTimeCalculator.setBitRate(bitRate);
+        mRecorder.setAudioEncodingBitRate(bitRate);
+    }
+
+    private void openOptionDialog(int optionType) {
+        final Context dialogContext = new ContextThemeWrapper(this,
+                android.R.style.Theme_DeviceDefault_Light_Dialog_Alert);
+        final Resources res = dialogContext.getResources();
+        final LayoutInflater dialogInflater = (LayoutInflater) dialogContext
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        final ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this,
+                android.R.layout.simple_list_item_single_choice) {
+            @Override
+            public View getView(int position, View convertView, ViewGroup parent) {
+                if (convertView == null) {
+                    convertView = dialogInflater.inflate(
+                            android.R.layout.simple_list_item_single_choice, parent, false);
+                }
+
+                final int resId = this.getItem(position);
+                ((TextView) convertView).setText(resId);
+                return convertView;
+            }
+        };
+        if (optionType == SETTING_TYPE_FILE_TYPE) {
+            adapter.add(R.string.format_setting_amr_item);
+            adapter.add(R.string.format_setting_3gpp_item);
+            adapter.add(R.string.format_setting_aac_item);
+            if (mWAVSupport) {
+                adapter.add(R.string.format_setting_wav_item);
+            }
+        } else if (optionType == SETTING_TYPE_STORAGE_LOCATION) {
+            adapter.add(R.string.storage_setting_local_item);
+            adapter.add(R.string.storage_setting_sdcard_item);
+        }
+
+        final DialogInterface.OnClickListener clickListener =
+                new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialog.dismiss();
+                        final int resId = adapter.getItem(which);
+                        switch (resId) {
+                            case R.string.format_setting_amr_item:
+                                mRequestedType = AUDIO_AMR;
+                                mFileType = 0;
+                                mPrefsStoragePathEditor.putString("requestedType", mRequestedType);
+                                mPrefsStoragePathEditor.putInt("fileType", mFileType);
+                                mPrefsStoragePathEditor.commit();
+                                break;
+                            case R.string.format_setting_3gpp_item:
+                                mRequestedType = AUDIO_3GPP;
+                                mFileType = 1;
+                                mPrefsStoragePathEditor.putString("requestedType", mRequestedType);
+                                mPrefsStoragePathEditor.putInt("fileType", mFileType);
+                                mPrefsStoragePathEditor.commit();
+                                // Keep 40KB size in the Recording file for Mpeg4Writer to write
+                                // Moov.
+                                if ((mMaxFileSize != -1) && (mMaxFileSize > 40 * 1024))
+                                    mMaxFileSize = mMaxFileSize - 40 * 1024;
+                                break;
+                            case R.string.format_setting_aac_item:
+                                mRequestedType = AUDIO_AAC_MP4;
+                                mFileType = 2;
+                                mPrefsStoragePathEditor.putString("requestedType", mRequestedType);
+                                mPrefsStoragePathEditor.putInt("fileType", mFileType);
+                                mPrefsStoragePathEditor.commit();
+                                break;
+                            case R.string.format_setting_wav_item:
+                                mRequestedType = AUDIO_WAVE_2CH_LPCM;
+                                mFileType = 3;
+                                mPrefsStoragePathEditor.putString("requestedType", mRequestedType);
+                                mPrefsStoragePathEditor.putInt("fileType", mFileType);
+                                mPrefsStoragePathEditor.commit();
+                                break;
+                            case R.string.storage_setting_sdcard_item:
+                                mStoragePath = StorageUtils.getSdStoragePath(SoundRecorder.this);
+                                mPath = StorageUtils.STORAGE_PATH_SD_INDEX;
+                                mPrefsStoragePathEditor.putString("storagePath", mStoragePath);
+                                mPrefsStoragePathEditor.putInt("path", mPath);
+                                mPrefsStoragePathEditor.commit();
+                                if (mPath == StorageUtils.STORAGE_PATH_SD_INDEX
+                                        && !StorageUtils.isSdMounted(SoundRecorder.this)) {
+                                    mSdExist = false;
+                                    mSampleInterrupted = true;
+                                    mErrorUiMessage = getResources()
+                                            .getString(R.string.insert_sd_card);
+                                } else {
+                                    mErrorUiMessage = null;
+                                }
+                                updateUi();
+                                break;
+                            case R.string.storage_setting_local_item:
+                                mSdExist = true;
+                                mStoragePath = StorageUtils.getPhoneStoragePath();
+                                mPath = StorageUtils.STORAGE_PATH_PHONE_INDEX;
+                                mPrefsStoragePathEditor.putString("storagePath", mStoragePath);
+                                mPrefsStoragePathEditor.putInt("path", mPath);
+                                mPrefsStoragePathEditor.commit();
+                                mSampleInterrupted = false;
+                                mErrorUiMessage = null;
+                                updateUi();
+                                break;
+
+                            default: {
+                                Log.e(TAG, "Unexpected resource: "
+                                        + getResources().getResourceEntryName(resId));
+                            }
+                        }
+                    }
+                };
+
+        AlertDialog ad = null;
+        if (optionType == SETTING_TYPE_STORAGE_LOCATION) {
+            ad = new AlertDialog.Builder(this)
+                    .setTitle(R.string.storage_setting)
+                    .setSingleChoiceItems(adapter, mPath, clickListener)
+                    .create();
+        } else if (optionType == SETTING_TYPE_FILE_TYPE) {
+            ad = new AlertDialog.Builder(this)
+                    .setTitle(R.string.format_setting)
+                    .setSingleChoiceItems(adapter, mFileType, clickListener)
+                    .create();
+        }
+        ad.setCanceledOnTouchOutside(true);
+        ad.show();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // TODO Auto-generated method stub
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.main_menu, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        menu.findItem(R.id.menu_item_keyboard).setEnabled(mRecorder.state() == Recorder.IDLE_STATE);
+        menu.findItem(R.id.menu_item_filetype).setEnabled(
+                (mRecorder.state() == Recorder.IDLE_STATE) && (!mExitAfterRecord));
+        menu.findItem(R.id.menu_item_storage).setEnabled(mRecorder.state() == Recorder.IDLE_STATE);
+        if (SystemPropertiesWrapper.getBoolean(VENDOR_SOUNDRECORDER_DEBUG_ENABLE, false)) {
+            menu.findItem(R.id.menu_item_keyboard).setVisible(true);
+        } else {
+            menu.findItem(R.id.menu_item_keyboard).setVisible(false);
+        }
+
+        if (mRecorderStop && !mRecorderProcessed) {
+            menu.findItem(R.id.menu_item_keyboard).setEnabled(false);
+            menu.findItem(R.id.menu_item_filetype).setEnabled(false);
+            menu.findItem(R.id.menu_item_storage).setEnabled(false);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // TODO Auto-generated method stub
+        switch (item.getItemId()) {
+            case R.id.menu_item_keyboard:
+                if(mRecorder.state() == Recorder.IDLE_STATE) {
+                    InputMethodManager inputMgr =
+                            (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+                    inputMgr.toggleSoftInput(0, 0);
                 }
                 break;
-            case R.id.playButton:
-                mRecorder.startPlayback();
+            case R.id.menu_item_filetype:
+                if(mRecorder.state() == Recorder.IDLE_STATE) {
+                    openOptionDialog(SETTING_TYPE_FILE_TYPE);
+                }
                 break;
-            case R.id.stopButton:
+            case R.id.menu_item_storage:
+                if(mRecorder.state() == Recorder.IDLE_STATE) {
+                    openOptionDialog(SETTING_TYPE_STORAGE_LOCATION);
+                }
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        //show softkeyboard after the "menu" key is pressed and released(key up)
+        if(keyCode == KeyEvent.KEYCODE_MENU) {
+            InputMethodManager inputMgr = (InputMethodManager)getSystemService(
+                                                    Context.INPUT_METHOD_SERVICE);
+            inputMgr.toggleSoftInput(0, 0);
+            return true;
+        } else {
+            return super.onKeyUp(keyCode, event);
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        switch (mRecorder.state()) {
+            case Recorder.IDLE_STATE:
+                super.onBackPressed();
+                break;
+            case Recorder.PAUSE_STATE:
+            case Recorder.RECORDING_STATE:
+                try {
+                    Thread.sleep(BACK_KEY_WAIT);
+                } catch (InterruptedException ex) {
+                }
                 mRecorder.stop();
-                break;
-            case R.id.acceptButton:
-                mRecorder.stop();
-                saveSample();
-                finish();
-                break;
-            case R.id.discardButton:
-                mRecorder.delete();
-                finish();
+                showRenameDialogIfNeed();
                 break;
         }
     }
-    
-    /*
-     * Handle the "back" hardware key. 
-     */
+
+    // Voicememo Adding UI choice for the user to get the format needed
     @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            switch (mRecorder.state()) {
-                case Recorder.IDLE_STATE:
-                    if (mRecorder.sampleLength() > 0)
-                        saveSample();
-                    finish();
-                    break;
-                case Recorder.PLAYING_STATE:
-                    mRecorder.stop();
-                    saveSample();
-                    break;
-                case Recorder.RECORDING_STATE:
-                    mRecorder.clear();
-                    break;
+    public boolean dispatchKeyEvent(KeyEvent event) {
+         Log.v(TAG, "dispatchKeyEvent with key event" + event);
+
+    if(event.getKeyCode() == KeyEvent.KEYCODE_6 && event.getAction() == event.ACTION_UP){
+       //Ignore ACTION_DOWN to avoid showing error dialog twice
+       if((mAudioSourceType == MediaRecorderWrapper.AudioSource.VOICE_CALL) ||
+          (mAudioSourceType == MediaRecorderWrapper.AudioSource.VOICE_DOWNLINK)||
+          (mAudioSourceType == MediaRecorderWrapper.AudioSource.VOICE_UPLINK ) ||
+          ((mAudioSourceType == MediaRecorderWrapper.AudioSource.MIC) &&
+           (mAudioManager.getMode() == AudioManager.MODE_IN_CALL))) {
+          mAudioSourceType = MediaRecorderWrapper.AudioSource.MIC;//Default type
+          Resources res = getResources();
+          String message = null;
+          message = res.getString(R.string.error_mediadb_aacincall);
+          new AlertDialog.Builder(this)
+          .setTitle(R.string.app_name)
+          .setMessage(message)
+          .setPositiveButton(R.string.button_ok, null)
+          .setCancelable(false)
+          .show();
+          return super.dispatchKeyEvent(event);
+       }
+    }
+
+    if((event.getKeyCode() == KeyEvent.KEYCODE_1 || event.getKeyCode() == KeyEvent.KEYCODE_2)
+         && (event.getAction() == event.ACTION_UP)){
+       //Ignore ACTION_DOWN to avoid showing error dialog twice
+        if((mAudioManager.getMode() != AudioManager.MODE_IN_CALL) ||
+               (AUDIO_AAC_MP4.equals(mRequestedType))) {
+
+            mAudioSourceType = MediaRecorderWrapper.AudioSource.MIC;//Default type
+            Resources res = getResources();
+            String message = null;
+
+            if(mAudioManager.getMode() != AudioManager.MODE_IN_CALL) {
+                message = res.getString(R.string.error_mediadb_incall);
+            } else {
+                message = res.getString(R.string.error_mediadb_aacincall);
             }
-            return true;
-        } else {
-            return super.onKeyDown(keyCode, event);
+
+            new AlertDialog.Builder(this)
+                .setTitle(R.string.app_name)
+                .setMessage(message)
+                .setPositiveButton(R.string.button_ok, null)
+                .setCancelable(false)
+                .show();
+
+            return super.dispatchKeyEvent(event);
         }
     }
+        // Intercept some events before they get dispatched to our views.
+        boolean ret = false;
+        switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_0: // MIC source (Camcorder)
+            {
+              Log.e(TAG, "Selected MIC Source: Key Event" + KeyEvent.KEYCODE_0);
+              mAudioSourceType = MediaRecorderWrapper.AudioSource.MIC;
+              if ((mAudioManager.getMode() == AudioManager.MODE_IN_CALL) &&
+                  (event.getAction() == event.ACTION_UP)) {
+                  mAudioSourceType = MediaRecorderWrapper.AudioSource.VOICE_UPLINK;
+                  Log.e(TAG, "Selected Voice Tx only Source: sourcetype" + mAudioSourceType);
+              }
+              ret = true;
+              break;
+            }
+
+            case KeyEvent.KEYCODE_1: // Voice Rx Only (Only during Call(
+            {
+              Log.e(TAG, "Selected Voice Rx only Source: Key Event" + KeyEvent.KEYCODE_1);
+              mAudioSourceType = MediaRecorderWrapper.AudioSource.VOICE_DOWNLINK;
+              ret = true;
+              break;
+            }
+
+            case KeyEvent.KEYCODE_2: // Voice Rx+Tx (Only during Call)
+            {
+              Log.e(TAG, "Selected Voice Tx+Rx Source: Key Event" + KeyEvent.KEYCODE_2);
+              mAudioSourceType = MediaRecorderWrapper.AudioSource.VOICE_CALL;
+              ret = true;
+              break;
+            }
+
+            case KeyEvent.KEYCODE_3: // Selected AMR codec type
+            {
+              Log.e(TAG, "Selected AUDIO_AMR Codec: Key Event" + KeyEvent.KEYCODE_3);
+              mRequestedType = AUDIO_AMR;
+              ret = true;
+              break;
+            }
+
+            case KeyEvent.KEYCODE_4: // Selected EVRC codec type
+            {
+              Log.e(TAG, "Selected Voice AUDIO_EVRC Codec: Key Event" + KeyEvent.KEYCODE_4);
+              mRequestedType = AUDIO_EVRC;
+              ret = true;
+              break;
+            }
+
+            case KeyEvent.KEYCODE_5: // Selected QCELP codec type
+            {
+              Log.e(TAG, "Selected AUDIO_QCELP Codec: Key Event" + KeyEvent.KEYCODE_5);
+              mRequestedType = AUDIO_QCELP;
+              ret = true;
+              break;
+            }
+            case KeyEvent.KEYCODE_6: // Selected AAC codec type
+            {
+              Log.e(TAG, "Selected AUDIO_AAC_MP4 Codec: Key Event" + KeyEvent.KEYCODE_6);
+              mRequestedType = AUDIO_AAC_MP4;
+              ret = true;
+              break;
+            }
+            case KeyEvent.KEYCODE_7: // Selected 6 channel wave lpcm codec type
+            {
+              if (true == bSSRSupported) {
+                Log.e(TAG, "Selected multichannel AAC Codec: Key Event" + KeyEvent.KEYCODE_7);
+                mRequestedType = AUDIO_AAC_5POINT1_CHANNEL;
+                ret = true;
+              }
+              break;
+            }
+            case KeyEvent.KEYCODE_8: // Selected 6 channel AAC recording
+            {
+                if (true == bSSRSupported) {
+                Log.e(TAG, "Selected linear pcm Codec: Key Event" + KeyEvent.KEYCODE_7);
+                mRequestedType = AUDIO_WAVE_6CH_LPCM;
+                ret = true;
+              }
+              break;
+            }
+            case KeyEvent.KEYCODE_9: // Selected amr-wb codec type in .awb file format
+            {
+              Log.e(TAG, "### Selected amr wb Codec in .awb: Key Event" + KeyEvent.KEYCODE_8);
+              mRequestedType = AUDIO_AMR_WB;
+              mAudioOutputFormat = MediaRecorderWrapper.OutputFormat.AMR_WB;
+              mAmrWidebandExtension = ".awb";
+              ret = true;
+              break;
+            }
+            case KeyEvent.KEYCODE_A: // Selected amr-wb codec type in .3gpp file format
+            {
+              Log.e(TAG, "### Selected awr wb Codec in 3gp: Key Event" + KeyEvent.KEYCODE_9);
+              mRequestedType = AUDIO_AMR_WB;
+              mAmrWidebandExtension = ".3gpp";
+              mAudioOutputFormat = MediaRecorderWrapper.OutputFormat.THREE_GPP;
+              ret = true;
+              break;
+            }
+
+            default:
+                break;
+        }
+
+        return ret?ret:super.dispatchKeyEvent(event);
+    }
 
     @Override
     public void onStop() {
@@ -449,32 +1198,118 @@
 
     @Override
     protected void onPause() {
-        mSampleInterrupted = mRecorder.state() == Recorder.RECORDING_STATE;
+        // Stop listening for phone state changes.
+        for(int i = 0; i < mPhoneCount; i++) {
+            // adapt case: disabled telephony feature or activate card failure
+            if (null != mPhoneStateListener[i]) {
+                mTelephonyManager.listen(mPhoneStateListener[i],
+                        PhoneStateListener.LISTEN_NONE);
+            }
+        }
         mRecorder.stop();
-        
+        // if dialog is shown, dialog processing the logic.
+        if (!mRenameDialogShown) {
+            if (mRecorder.sampleLength() > 0) {
+                mRecorderStop = true;
+                saveSample(true);
+            } else {
+                mRecorder.delete();
+            }
+        }
         super.onPause();
     }
 
     /*
-     * If we have just recorded a smaple, this adds it to the media data base
+     * If we have just recorded a sample, 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;
+    private boolean saveSample(boolean showToast) {
         Uri uri = null;
+
+        if (mRecorder.sampleLength() <= 0) {
+            mRecorder.delete();
+            return false;
+        }
+
         try {
-            uri = this.addToMediaDB(mRecorder.sampleFile());
+            mDataExist = DatabaseUtils.isDataExist(getContentResolver(), mRecorder.sampleFile());
+            if (!mDataExist) {
+                uri = DatabaseUtils.addToMediaDB(SoundRecorder.this, mRecorder.sampleFile(),
+                        mRecorder.sampleLengthMillis(), mRequestedType);
+            }
         } catch(UnsupportedOperationException ex) {  // Database manipulation failure
-            return;
+            return false;
+        } finally {
+            if (uri == null && !mDataExist) {
+                return false;
+            }
         }
-        if (uri == null) {
-            return;
+
+        if (showToast) {
+            showSavedToast();
         }
+
+        // reset mRecorder and restore UI.
+        mRecorder.clear();
+        updateUi();
         setResult(RESULT_OK, new Intent().setData(uri)
-                                         .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
+                .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
+        return true;
     }
-    
+
+    private String getLastFileName(boolean withExtension) {
+        return FileUtils.getLastFileName(mRecorder.sampleFile(), withExtension);
+    }
+
+    // Show a dialog to rename file name.
+    private void showRenameDialogIfNeed() {
+        if (mRecorder == null) return;
+        if (mRecorder.sampleLength() > 0) {
+            mRecorderStop = true;
+            RenameDialogBuilder builder = new RenameDialogBuilder(this, mRecorder.sampleFile());
+            builder.setTitle(R.string.save_dialog_title);
+            builder.setNegativeButton(R.string.discard, null);
+            builder.setPositiveButton(R.string.rename_dialog_save,
+                    new RenameDialogBuilder.OnPositiveListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which, String newName) {
+                            if (acceptSample(newName)) {
+                                startListActivity();
+                            }
+                        }
+                    });
+            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                @Override
+                public void onDismiss(DialogInterface dialog) {
+                    discardSample();
+                    mRenameDialogShown = false;
+                }
+            });
+            builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+                @Override
+                public void onCancel(DialogInterface dialog) {
+                    discardSample();
+                    mRenameDialogShown = false;
+                }
+            });
+            builder.setEditTextContent(getLastFileName(false));
+            builder.show();
+            mRenameDialogShown = true;
+        } else {
+            mRecorder.delete();
+        }
+    }
+
+    private void startListActivity() {
+        Intent intent = new Intent(SoundRecorder.this, FileListActivity.class);
+        startActivity(intent);
+    }
+
+    private void showSavedToast() {
+        String info = getResources().getString(R.string.file_saved_interrupt);
+        Toast.makeText(SoundRecorder.this, info, Toast.LENGTH_SHORT).show();
+    }
+
     /*
      * Called on destroy to unregister the SD card mount event receiver.
      */
@@ -484,9 +1319,50 @@
             unregisterReceiver(mSDCardMountEventReceiver);
             mSDCardMountEventReceiver = null;
         }
+        if (mPowerOffReceiver != null) {
+            unregisterReceiver(mPowerOffReceiver);
+            mPowerOffReceiver = null;
+        }
+        unregisterReceiver(mMountReceiver);
+
+        if (null != mProgressDialog && mProgressDialog.isShowing()) {
+            mProgressDialog.dismiss();
+            mProgressDialog = null;
+        }
+
+        if (mMsgHandler.hasMessages(MSG_DISMISS_PROGRESS_DIALOG)) {
+            mMsgHandler.removeMessages(MSG_DISMISS_PROGRESS_DIALOG);
+        }
+
         super.onDestroy();
     }
-    
+
+    /*
+     * Registers an intent to listen for ACTION_SHUTDOWN notifications.
+     */
+    private void registerPowerOffListener() {
+        if (mPowerOffReceiver == null) {
+            mPowerOffReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    String action = intent.getAction();
+
+                    if (mRecorder != null) {
+                        mRecorder.stop();
+                    }
+
+                    if (action.equals(Intent.ACTION_SHUTDOWN)) {
+                        mRecorder.delete();
+                    }
+                }
+            };
+            IntentFilter iFilter = new IntentFilter();
+            iFilter.addAction(Intent.ACTION_SHUTDOWN);
+            iFilter.addAction(Intent.ACTION_SCREEN_OFF);
+            registerReceiver(mPowerOffReceiver, iFilter);
+        }
+    }
+
     /*
      * Registers an intent to listen for ACTION_MEDIA_EJECT/ACTION_MEDIA_MOUNTED
      * notifications.
@@ -500,8 +1376,7 @@
                     if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
                         mRecorder.delete();
                     } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
-                        mSampleInterrupted = false;
-                        updateUi();
+                        mRecorder.delete();
                     }
                 }
             };
@@ -513,349 +1388,273 @@
         }
     }
 
-    /*
-     * A simple utility to do a query into the databases.
-     */
-    private Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
-        try {
-            ContentResolver resolver = getContentResolver();
-            if (resolver == null) {
-                return null;
-            }
-            return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
-         } catch (UnsupportedOperationException ex) {
-            return null;
-        }
-    }
-    
-    /*
-     * Add the given audioId to the playlist with the given playlistId; and maintain the
-     * play_order in the playlist.
-     */
-    private void addToPlaylist(ContentResolver resolver, int audioId, long playlistId) {
-        String[] cols = new String[] {
-                "count(*)"
-        };
-        Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
-        Cursor cur = resolver.query(uri, cols, null, null, null);
-        cur.moveToFirst();
-        final int base = cur.getInt(0);
-        cur.close();
-        ContentValues values = new ContentValues();
-        values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(base + audioId));
-        values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
-        resolver.insert(uri, values);
-    }
-    
-    /*
-     * Obtain the id for the default play list from the audio_playlists table.
-     */
-    private int getPlaylistId(Resources res) {
-        Uri uri = MediaStore.Audio.Playlists.getContentUri("external");
-        final String[] ids = new String[] { MediaStore.Audio.Playlists._ID };
-        final String where = MediaStore.Audio.Playlists.NAME + "=?";
-        final String[] args = new String[] { res.getString(R.string.audio_db_playlist_name) };
-        Cursor cursor = query(uri, ids, where, args, null);
-        if (cursor == null) {
-            Log.v(TAG, "query returns null");
-        }
-        int id = -1;
-        if (cursor != null) {
-            cursor.moveToFirst();
-            if (!cursor.isAfterLast()) {
-                id = cursor.getInt(0);
-            }
-        }
-        cursor.close();
-        return id;
-    }
-    
-    /*
-     * Create a playlist with the given default playlist name, if no such playlist exists.
-     */
-    private Uri createPlaylist(Resources res, ContentResolver resolver) {
-        ContentValues cv = new ContentValues();
-        cv.put(MediaStore.Audio.Playlists.NAME, res.getString(R.string.audio_db_playlist_name));
-        Uri uri = resolver.insert(MediaStore.Audio.Playlists.getContentUri("external"), cv);
-        if (uri == null) {
-            new AlertDialog.Builder(this)
-                .setTitle(R.string.app_name)
-                .setMessage(R.string.error_mediadb_new_record)
-                .setPositiveButton(R.string.button_ok, null)
-                .setCancelable(false)
-                .show();
-        }
-        return uri;
-    }
-
-    /*
-     * Adds file and returns content uri.
-     */
-    private Uri addToMediaDB(File file) {
-        Resources res = getResources();
-        ContentValues cv = new ContentValues();
-        long current = System.currentTimeMillis();
-        long modDate = file.lastModified();
-        Date date = new Date(current);
-        SimpleDateFormat formatter = new SimpleDateFormat(
-                res.getString(R.string.audio_db_title_format));
-        String title = formatter.format(date);
-        long sampleLengthMillis = mRecorder.sampleLength() * 1000L;
-
-        // Lets label the recorded audio file as NON-MUSIC so that the file
-        // won't be displayed automatically, except for in the playlist.
-        cv.put(MediaStore.Audio.Media.IS_MUSIC, "0");
-
-        cv.put(MediaStore.Audio.Media.TITLE, title);
-        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.Media.DURATION, sampleLengthMillis);
-        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,
-                res.getString(R.string.audio_db_album_name));
-        Log.d(TAG, "Inserting audio record: " + cv.toString());
-        ContentResolver resolver = getContentResolver();
-        Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
-        Log.d(TAG, "ContentURI: " + base);
-        Uri result = resolver.insert(base, cv);
-        if (result == null) {
-            new AlertDialog.Builder(this)
-                .setTitle(R.string.app_name)
-                .setMessage(R.string.error_mediadb_new_record)
-                .setPositiveButton(R.string.button_ok, null)
-                .setCancelable(false)
-                .show();
-            return null;
-        }
-        if (getPlaylistId(res) == -1) {
-            createPlaylist(res, resolver);
-        }
-        int audioId = Integer.valueOf(result.getLastPathSegment());
-        addToPlaylist(resolver, audioId, getPlaylistId(res));
-
-        // Notify those applications such as Music listening to the 
-        // scanner events that a recorded audio file just created. 
-        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
-        return result;
-    }
-
     /**
      * Update the big MM:SS timer. If we are in playback, also update the
      * progress bar.
      */
     private void updateTimerView() {
-        Resources res = getResources();
         int state = mRecorder.state();
-        
-        boolean ongoing = state == Recorder.RECORDING_STATE || state == Recorder.PLAYING_STATE;
-        
+
+        boolean ongoing = state == Recorder.RECORDING_STATE;
+
         long time = ongoing ? mRecorder.progress() : mRecorder.sampleLength();
-        String timeStr = String.format(mTimerFormat, time/60, time%60);
-        mTimerView.setText(timeStr);
-        
-        if (state == Recorder.PLAYING_STATE) {
-            mStateProgressBar.setProgress((int)(100*time/mRecorder.sampleLength()));
-        } else if (state == Recorder.RECORDING_STATE) {
+        mTimerView.setText(Utils.timeToString(mTimerView.getContext(), time));
+
+        if (state == Recorder.RECORDING_STATE) {
             updateTimeRemaining();
         }
-                
+
         if (ongoing)
             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. 
+     * 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() {
         long t = mRemainingTimeCalculator.timeRemaining();
-            
+
         if (t <= 0) {
             mSampleInterrupted = true;
 
             int limit = mRemainingTimeCalculator.currentLowerLimit();
             switch (limit) {
                 case RemainingTimeCalculator.DISK_SPACE_LIMIT:
-                    mErrorUiMessage 
+                    mErrorUiMessage
                         = getResources().getString(R.string.storage_is_full);
                     break;
                 case RemainingTimeCalculator.FILE_SIZE_LIMIT:
-                    mErrorUiMessage 
+                    mErrorUiMessage
                         = getResources().getString(R.string.max_length_reached);
                     break;
                 default:
                     mErrorUiMessage = null;
                     break;
             }
-            
+
             mRecorder.stop();
+            showRenameDialogIfNeed();
             return;
         }
-            
+
         Resources res = getResources();
         String timeStr = "";
-        
-        if (t < 60)
-            timeStr = String.format(res.getString(R.string.sec_available), t);
-        else if (t < 540)
-            timeStr = String.format(res.getString(R.string.min_available), t/60 + 1);
-        
+
+        // display available time, if less than 10 minutes
+        if (t < 599)
+        {
+            timeStr = String.format(mTimerFormat, t / 60, t % 60);
+        }
+
         mStateMessage1.setText(timeStr);
     }
-    
+
     /**
      * Shows/hides the appropriate child views for the new state.
      */
     private void updateUi() {
         Resources res = getResources();
-        
+
         switch (mRecorder.state()) {
             case Recorder.IDLE_STATE:
                 if (mRecorder.sampleLength() == 0) {
+                    mRecordButton.setImageResource(R.drawable.record);
                     mRecordButton.setEnabled(true);
-                    mRecordButton.setFocusable(true);
-                    mPlayButton.setEnabled(false);
-                    mPlayButton.setFocusable(false);
+                    mRecordButton.setFocusable(false);
                     mStopButton.setEnabled(false);
                     mStopButton.setFocusable(false);
-                    mRecordButton.requestFocus();
-                    
-                    mStateMessage1.setVisibility(View.INVISIBLE);
-                    mStateLED.setVisibility(View.INVISIBLE);
-                    mStateMessage2.setVisibility(View.INVISIBLE);
-                    
-                    mExitButtons.setVisibility(View.INVISIBLE);
-                    mVUMeter.setVisibility(View.VISIBLE);
 
-                    mStateProgressBar.setVisibility(View.INVISIBLE);
-                    
-                    setTitle(res.getString(R.string.record_your_message));                    
+                    mStateMessage1.setVisibility(View.INVISIBLE);
+                    mStateMessage2.setVisibility(View.VISIBLE);
+                    if (true == bSSRSupported) {
+                        mStateMessage2.setText(res.getString(R.string.press_record_ssr));
+                    } else {
+                        if (SystemPropertiesWrapper.getBoolean(VENDOR_SOUNDRECORDER_DEBUG_ENABLE, false)) {
+                            mStateMessage2.setText(res.getString(R.string.press_record));
+                        } else {
+                            mStateMessage2.setText(res.getString(R.string.press_record2));
+                        }
+                    }
+                    mVUMeter.resetAngle();
+
+                    setTitle(res.getString(R.string.record_your_message));
                 } else {
+                    mRecordButton.setImageResource(R.drawable.record);
                     mRecordButton.setEnabled(true);
                     mRecordButton.setFocusable(true);
-                    mPlayButton.setEnabled(true);
-                    mPlayButton.setFocusable(true);
                     mStopButton.setEnabled(false);
                     mStopButton.setFocusable(false);
-                                            
+
                     mStateMessage1.setVisibility(View.INVISIBLE);
-                    mStateLED.setVisibility(View.INVISIBLE);                        
-                    mStateMessage2.setVisibility(View.INVISIBLE);
-
-                    mExitButtons.setVisibility(View.VISIBLE);
-                    mVUMeter.setVisibility(View.INVISIBLE);
-
-                    mStateProgressBar.setVisibility(View.INVISIBLE);
+                    mStateMessage2.setVisibility(View.VISIBLE);
+                    mStateMessage2.setText(res.getString(R.string.recording_stopped));
 
                     setTitle(res.getString(R.string.message_recorded));
                 }
-                
+
                 if (mSampleInterrupted) {
+                    //TODO: Set decent message and icon resources
                     mStateMessage2.setVisibility(View.VISIBLE);
                     mStateMessage2.setText(res.getString(R.string.recording_stopped));
-                    mStateLED.setVisibility(View.INVISIBLE);
                 }
-                
+
                 if (mErrorUiMessage != null) {
                     mStateMessage1.setText(mErrorUiMessage);
                     mStateMessage1.setVisibility(View.VISIBLE);
                 }
-                
+
+                // disable list button if start from ACTION_GET_CONTENT
+                if (mIsGetContentAction) {
+                    mListButton.setEnabled(false);
+                    mListButton.setFocusable(false);
+                } else {
+                    mListButton.setEnabled(true);
+                    mListButton.setFocusable(true);
+                }
                 break;
-            case Recorder.RECORDING_STATE: 
-                mRecordButton.setEnabled(false);
-                mRecordButton.setFocusable(false);
-                mPlayButton.setEnabled(false);
-                mPlayButton.setFocusable(false);
+            case Recorder.RECORDING_STATE:
+                mRecordButton.setImageResource(R.drawable.pause);
+                mRecordButton.setEnabled(true);
+                mRecordButton.setFocusable(true);
                 mStopButton.setEnabled(true);
                 mStopButton.setFocusable(true);
-                
+                mListButton.setEnabled(false);
+                mListButton.setFocusable(false);
+
                 mStateMessage1.setVisibility(View.VISIBLE);
-                mStateLED.setVisibility(View.VISIBLE);
-                mStateLED.setImageResource(R.drawable.recording_led);
                 mStateMessage2.setVisibility(View.VISIBLE);
                 mStateMessage2.setText(res.getString(R.string.recording));
-                
-                mExitButtons.setVisibility(View.INVISIBLE);
-                mVUMeter.setVisibility(View.VISIBLE);
 
-                mStateProgressBar.setVisibility(View.INVISIBLE);
-                
                 setTitle(res.getString(R.string.record_your_message));
 
                 break;
-
-            case Recorder.PLAYING_STATE: 
+            case Recorder.PAUSE_STATE:
+                mRecordButton.setImageResource(R.drawable.record);
                 mRecordButton.setEnabled(true);
                 mRecordButton.setFocusable(true);
-                mPlayButton.setEnabled(false);
-                mPlayButton.setFocusable(false);
                 mStopButton.setEnabled(true);
                 mStopButton.setFocusable(true);
-                
-                mStateMessage1.setVisibility(View.INVISIBLE);
-                mStateLED.setVisibility(View.INVISIBLE);
-                mStateMessage2.setVisibility(View.INVISIBLE);
-                
-                mExitButtons.setVisibility(View.VISIBLE);
-                mVUMeter.setVisibility(View.INVISIBLE);
+                mListButton.setEnabled(false);
+                mListButton.setFocusable(false);
 
-                mStateProgressBar.setVisibility(View.VISIBLE);
+                mStateMessage1.setVisibility(View.VISIBLE);
+                mStateMessage2.setVisibility(View.VISIBLE);
+                mStateMessage2.setText(res.getString(R.string.recording_paused));
 
-                setTitle(res.getString(R.string.review_message));
+                mVUMeter.resetAngle();
+
+                setTitle(res.getString(R.string.record_your_message));
 
                 break;
         }
-        
-        updateTimerView();   
+
+        // If the SD card does not exist and mPath is SD card, disable the record button.
+        if (mPath == StorageUtils.STORAGE_PATH_SD_INDEX && !mSdExist) {
+            mStateMessage2.setText("");
+            mRecordButton.setEnabled(false);
+            mRecordButton.setFocusable(false);
+        }
+        if (mErrorUiMessage == null) {
+            mStateMessage1.setText("");
+        }
+        updateTimerView();
         mVUMeter.invalidate();
     }
-    
+
     /*
      * Called when Recorder changed it's state.
      */
     public void onStateChanged(int state) {
-        if (state == Recorder.PLAYING_STATE || state == Recorder.RECORDING_STATE) {
+        if (state == Recorder.RECORDING_STATE) {
             mSampleInterrupted = false;
             mErrorUiMessage = null;
-            mWakeLock.acquire(); // we don't want to go to sleep while recording or playing
+        }
+
+        if (state == Recorder.RECORDING_STATE) {
+            mWakeLock.acquire(); // we don't want to go to sleep while recording
         } else {
             if (mWakeLock.isHeld())
                 mWakeLock.release();
         }
-        
-        updateUi();
+
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                updateUi();
+                invalidateOptionsMenu();
+            }
+        });
     }
-    
+
     /*
      * Called when MediaPlayer encounters an error.
      */
     public void onError(int error) {
-        Resources res = getResources();
-        
-        String message = null;
-        switch (error) {
-            case Recorder.SDCARD_ACCESS_ERROR:
-                message = res.getString(R.string.error_sdcard_access);
-                break;
-            case Recorder.IN_CALL_RECORD_ERROR:
-                // TODO: update error message to reflect that the recording could not be
-                //       performed during a call.
-            case Recorder.INTERNAL_ERROR:
-                message = res.getString(R.string.error_app_internal);
-                break;
-        }
-        if (message != null) {
-            new AlertDialog.Builder(this)
-                .setTitle(R.string.app_name)
-                .setMessage(message)
-                .setPositiveButton(R.string.button_ok, null)
-                .setCancelable(false)
-                .show();
-        }
+        final int iError = error;
+
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Resources res = getResources();
+                boolean isExit = false;
+
+                String message = null;
+                switch (iError) {
+                    case Recorder.RECORD_INTERRUPTED:
+                        message = res.getString(R.string.error_record_interrupted);
+                        break;
+                    case Recorder.SDCARD_ACCESS_ERROR:
+                        message = res.getString(R.string.error_sdcard_access);
+                        break;
+                    case Recorder.IN_CALL_RECORD_ERROR:
+                        // TODO: update error message to reflect that the recording could not be
+                        //       performed during a call.
+                        message = res.getString(R.string.in_call_record_error);
+                        isExit = true;
+                        break;
+                    case Recorder.INTERNAL_ERROR:
+                        message = res.getString(R.string.error_app_internal);
+                        isExit = true;
+                        break;
+                    case Recorder.UNSUPPORTED_FORMAT:
+                        message = res.getString(R.string.error_app_unsupported);
+                        isExit = true;
+                        break;
+                    case Recorder.RECORD_LOST_FOCUS:
+                        showRenameDialogIfNeed();
+                        break;
+                }
+                if (message != null) {
+                    new AlertDialog.Builder(SoundRecorder.this)
+                            .setTitle(R.string.app_name)
+                            .setMessage(message)
+                            .setPositiveButton(R.string.button_ok, (true==isExit)?
+                                    (new DialogInterface.OnClickListener() {
+                                        public void onClick(DialogInterface dialog, int whichButton) {
+                                            finish();
+                                        }}):null)
+                            .setCancelable(false)
+                            .show();
+                }
+            }
+        });
+    }
+
+    public void onInfo(int what, int extra) {
+        final int iWhat = what;
+
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (iWhat == MediaRecorderWrapper.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
+                    mRecorder.stop();
+                    showRenameDialogIfNeed();
+                    mVUMeter.resetAngle();
+                    invalidateOptionsMenu();
+                }
+            }
+        });
     }
 }
diff --git a/src/com/android/soundrecorder/VUMeter.java b/src/com/android/soundrecorder/VUMeter.java
index 0396284..53e1308 100644
--- a/src/com/android/soundrecorder/VUMeter.java
+++ b/src/com/android/soundrecorder/VUMeter.java
@@ -16,32 +16,57 @@
 
 package com.android.soundrecorder;
 
-import java.util.Map;
-
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Color;
+import android.graphics.DashPathEffect;
 import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
+import android.graphics.PathEffect;
 import android.util.AttributeSet;
 import android.view.View;
 
 public class VUMeter extends View {
-    static final float PIVOT_RADIUS = 3.5f;
-    static final float PIVOT_Y_OFFSET = 10f;
-    static final float SHADOW_OFFSET = 2.0f;
     static final float DROPOFF_STEP = 0.18f;
-    static final float SURGE_STEP = 0.35f;
-    static final long  ANIMATION_INTERVAL = 70;
-    
-    Paint mPaint, mShadow;
-    float mCurrentAngle;
-    
+    static final long ANIMATION_INTERVAL = 70;
+    static final int MAX_AMPLITUDE = 32768;
+
+    static final float MIN_ANGLE = (float) Math.PI * -27 / 100;
+    static final float MAX_ANGLE = (float) Math.PI * 127 / 100;
+    static final float MIN_DEGREES = radiansToDegrees(MIN_ANGLE);
+    static final float MAX_DEGREES = radiansToDegrees(MAX_ANGLE);
+
+    static final float START_DEGREES = MIN_DEGREES + 180;
+    static final float MASK_DEGREES_OFFSET = 0.4f;
+
+    float mCurrentAngle = MIN_ANGLE;
+
+    private Paint mPaint;
+    private Paint mOuterPaint;
+    private Paint mProgressPaint;
+    private Paint mDashedProgressPaint;
+    private final int mMaskColor =
+            getContext().getResources().getColor(R.color.vumeter_background_color);
+    private final int mMainColor =
+            getContext().getResources().getColor(R.color.vumeter_color);
+    private final int mProgressColor =
+            getContext().getResources().getColor(R.color.vumeter_progress_color);
+    private final int mOuterWidth = getContext().getResources().
+            getDimensionPixelSize(R.dimen.vumeter_outer_width);
+    private final int mOuterInnerMargin = getContext().getResources().
+            getDimensionPixelSize(R.dimen.vumeter_outer_inner_margin);
+    private final int mProgressWidth = getContext().getResources().
+            getDimensionPixelSize(R.dimen.vumeter_progress_width);
+    private final int mProgressDashedWidth = getContext().getResources().
+            getDimensionPixelSize(R.dimen.vumeter_progress_dashed_width);
+    private final PathEffect mDashedEffects = new DashPathEffect(
+            new float[]{
+                    mProgressDashedWidth,
+                    mProgressWidth
+            }, 0);
+
     Recorder mRecorder;
 
     public VUMeter(Context context) {
-        super(context);
-        init(context);
+        this(context, null);
     }
 
     public VUMeter(Context context, AttributeSet attrs) {
@@ -50,57 +75,97 @@
     }
 
     void init(Context context) {
-        Drawable background = context.getResources().getDrawable(R.drawable.vumeter);
-        setBackgroundDrawable(background);
-        
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mPaint.setColor(Color.WHITE);
-        mShadow = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mShadow.setColor(Color.argb(60, 0, 0, 0));
-        
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeWidth(mProgressWidth);
+        mPaint.setColor(mMainColor);
+
+        mOuterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mOuterPaint.setStyle(Paint.Style.STROKE);
+        mOuterPaint.setStrokeWidth(mOuterWidth);
+        mOuterPaint.setColor(mMainColor);
+
+        mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mProgressPaint.setStyle(Paint.Style.STROKE);
+        mProgressPaint.setStrokeWidth(mProgressWidth);
+        mProgressPaint.setColor(mProgressColor);
+
+        mDashedProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mDashedProgressPaint.setStyle(Paint.Style.STROKE);
+        mDashedProgressPaint.setStrokeWidth(mProgressWidth + 1); // +1 to make sure cover bottom.
+        mDashedProgressPaint.setPathEffect(mDashedEffects);
+        mDashedProgressPaint.setColor(mMaskColor);
+
         mRecorder = null;
-        
-        mCurrentAngle = 0;
+
+        mCurrentAngle = MIN_ANGLE;
+    }
+
+    public void resetAngle() {
+        mCurrentAngle = MIN_ANGLE;
+        invalidate();
     }
 
     public void setRecorder(Recorder recorder) {
-    	mRecorder = recorder;
-    	invalidate();
+        mRecorder = recorder;
+        invalidate();
     }
-    
+
     @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
 
-        final float minAngle = (float)Math.PI/8;
-        final float maxAngle = (float)Math.PI*7/8;
-                
-        float angle = minAngle;
+        float angle = MIN_ANGLE;
         if (mRecorder != null)
-        	angle += (float)(maxAngle - minAngle)*mRecorder.getMaxAmplitude()/32768;
+            angle += (MAX_ANGLE - MIN_ANGLE) * mRecorder.getMaxAmplitude() / MAX_AMPLITUDE;
 
-        if (angle > mCurrentAngle)
+        if (angle >= mCurrentAngle)
             mCurrentAngle = angle;
         else
             mCurrentAngle = Math.max(angle, mCurrentAngle - DROPOFF_STEP);
 
-        mCurrentAngle = Math.min(maxAngle, mCurrentAngle);
+        mCurrentAngle = Math.min(MAX_ANGLE, mCurrentAngle);
 
         float w = getWidth();
         float h = getHeight();
-        float pivotX = w/2;
-        float pivotY = h - PIVOT_RADIUS - PIVOT_Y_OFFSET;
-        float l = h*4/5;
-        float sin = (float) Math.sin(mCurrentAngle);
-        float cos = (float) Math.cos(mCurrentAngle);
-        float x0 = pivotX - l*cos;
-        float y0 = pivotY - l*sin;
-        canvas.drawLine(x0 + SHADOW_OFFSET, y0 + SHADOW_OFFSET, pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, mShadow);
-        canvas.drawCircle(pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, PIVOT_RADIUS, mShadow);
-        canvas.drawLine(x0, y0, pivotX, pivotY, mPaint);
-        canvas.drawCircle(pivotX, pivotY, PIVOT_RADIUS, mPaint);
-        
+        float size = Math.min(w, h);
+
+        // outer arc
+        float left = (w - size) / 2 + mOuterWidth / 2;
+        float top = (h - size) / 2 + mOuterWidth / 2;
+        float right = left + size - mOuterWidth;
+        float bottom = top + size - mOuterWidth;
+        canvas.drawArc(left, top, right, bottom,
+                START_DEGREES, MAX_DEGREES - MIN_DEGREES, false, mOuterPaint);
+
+        left = left + mOuterInnerMargin;
+        top = top + mOuterInnerMargin;
+        right = right - mOuterInnerMargin;
+        bottom = bottom - mOuterInnerMargin;
+        // progress background arc
+        canvas.drawArc(left, top, right, bottom,
+                START_DEGREES, MAX_DEGREES - MIN_DEGREES, false, mPaint);
+        // progress arc
+        float sweepDegrees = radiansToDegrees(mCurrentAngle - MIN_ANGLE);
+        if (sweepDegrees > 0) {
+            canvas.drawArc(left, top, right, bottom,
+                    START_DEGREES, sweepDegrees, false, mProgressPaint);
+        }
+
+        // progress background mask arc
+        canvas.drawArc(left, top, right, bottom,
+                START_DEGREES - MASK_DEGREES_OFFSET,
+                MAX_DEGREES - MIN_DEGREES - MASK_DEGREES_OFFSET * 2, false, mDashedProgressPaint);
+
         if (mRecorder != null && mRecorder.state() == Recorder.RECORDING_STATE)
-        	postInvalidateDelayed(ANIMATION_INTERVAL);
+            postInvalidateDelayed(ANIMATION_INTERVAL);
+    }
+
+    private static float radiansToDegrees(double radians) {
+        return (float) (radians / Math.PI * 180);
+    }
+
+    private static float degreesToRadians(float degrees) {
+        return (float) (degrees / 180 * Math.PI);
     }
 }
diff --git a/src/com/android/soundrecorder/actionmode/ActionModeHandler.java b/src/com/android/soundrecorder/actionmode/ActionModeHandler.java
new file mode 100644
index 0000000..73d550a
--- /dev/null
+++ b/src/com/android/soundrecorder/actionmode/ActionModeHandler.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.actionmode;
+
+import android.app.Activity;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.PopupMenu;
+import android.widget.Spinner;
+
+import com.android.soundrecorder.R;
+import com.android.soundrecorder.filelist.FileListRecyclerAdapter;
+import com.android.soundrecorder.filelist.listitem.BaseListItem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ActionModeHandler implements FileListRecyclerAdapter.ActionModeListener {
+    private Activity mActivity;
+    private View mCustomView;
+    private ButtonWithPopupMenu mSelectionButton;
+    private String mSelectedFormat;
+    private ActionMode mActionMode;
+    private ActionMode.Callback mCallback;
+
+    private Spinner mSelectionSpinner;
+    private String mSelectedAllString;
+    private String mDeSelectedAllString;
+    private static final int SPINNER_COUNT_INDEX = 0;
+    private static final int SPINNER_SELECT_ALL_INDEX = 1;
+
+    public ActionModeHandler(Activity activity, int customViewId, ActionMode.Callback callback) {
+        mActivity = activity;
+        mCallback = callback;
+        mCustomView = LayoutInflater.from(activity).inflate(customViewId, null);
+        mSelectedAllString = mCustomView.getResources().getString(R.string.action_mode_select_all);
+        mDeSelectedAllString = mCustomView.getResources()
+                .getString(R.string.action_mode_deselect_all);
+        mSelectionButton = (ButtonWithPopupMenu) mCustomView.findViewById(R.id.selection_button);
+        mSelectionButton.loadPopupMenu(R.menu.select_all_popup_menu, mOnSelectAllClickListener);
+        mSelectedFormat = mCustomView.getResources().getString(
+                R.string.action_mode_selected);
+
+        mSelectionSpinner = (Spinner) mCustomView.findViewById(R.id.selection_spinner);
+
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        mCustomView.setLayoutParams(lp);
+    }
+
+    @Override
+    public void showActionMode() {
+        mActionMode = mActivity.startActionMode(mCallback);
+        if (mActionMode != null) {
+            mActionMode.setCustomView(mCustomView);
+        }
+    }
+
+    @Override
+    public void exitActionMode() {
+        if (mActionMode != null) {
+            mActionMode.finish();
+        }
+    }
+
+    @Override
+    public void setSelectedCount(int selectedCount, int totalCount, List<BaseListItem> items) {
+        updateSelectionMenu(selectedCount, totalCount);
+        updateActionModeOperations(selectedCount, items);
+    }
+
+    private void updateSelectionMenu(int selectedCount, int totalCount) {
+        List<String> list = new ArrayList<String>();
+        list.add(String.format(mSelectedFormat, selectedCount));
+        list.add(selectedCount >= totalCount ? mDeSelectedAllString : mSelectedAllString);
+        ArrayAdapter adapter = new ArrayAdapter<>(mSelectionSpinner.getContext(),
+                R.layout.spinner_dropdown_item, list);
+        mSelectionSpinner.setAdapter(adapter);
+        mSelectionSpinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                if (position == SPINNER_SELECT_ALL_INDEX) {
+                    if (mCallback != null && mActionMode != null) {
+                        MenuItem item = mActionMode.getMenu().findItem(R.id.action_select_all);
+                        mCallback.onActionItemClicked(mActionMode, item);
+                    }
+                }
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+            }
+        });
+        mSelectionSpinner.setSelection(SPINNER_COUNT_INDEX, true);
+
+        mSelectionButton.setText(String.format(mSelectedFormat, selectedCount));
+        MenuItem menuItem = mSelectionButton.findPopupMenuItem(R.id.action_select_all);
+        menuItem.setTitle(selectedCount >= totalCount ? mDeSelectedAllString : mSelectedAllString);
+    }
+
+    private void updateActionModeOperations(int selectedCount, List<BaseListItem> items) {
+        if (mActionMode != null) {
+            int operation = BaseListItem.SUPPORT_ALL;
+            for (BaseListItem item : items) {
+                operation &= item.getSupportedOperation();
+            }
+
+            if (selectedCount == 0) {
+                operation = BaseListItem.SUPPORT_NONE;
+            } else if (selectedCount > 1) {
+                operation &= ~BaseListItem.SUPPORT_EDIT;
+            }
+            updateMenuOperation(mActionMode.getMenu(), operation);
+        }
+    }
+
+    private void updateMenuOperation(Menu menu, int supported) {
+        boolean supportDelete = (supported & BaseListItem.SUPPORT_DELETE) != 0;
+        boolean supportShare = (supported & BaseListItem.SUPPORT_SHARE) != 0;
+        boolean supportEdit = (supported & BaseListItem.SUPPORT_EDIT) != 0;
+        setMenuItemVisible(menu, R.id.action_delete, supportDelete);
+        setMenuItemVisible(menu, R.id.action_share, supportShare);
+        setMenuItemVisible(menu, R.id.action_edit, supportEdit);
+    }
+
+    private void setMenuItemVisible(Menu menu, int itemId, boolean visible) {
+        MenuItem item = menu.findItem(itemId);
+        if (item != null) item.setVisible(visible);
+    }
+
+    private PopupMenu.OnMenuItemClickListener mOnSelectAllClickListener =
+            new PopupMenu.OnMenuItemClickListener() {
+                @Override
+                public boolean onMenuItemClick(MenuItem item) {
+                    if (mCallback != null && mActionMode != null) {
+                        return mCallback.onActionItemClicked(mActionMode, item);
+                    } else {
+                        return false;
+                    }
+                }
+            };
+}
diff --git a/src/com/android/soundrecorder/actionmode/ButtonWithPopupMenu.java b/src/com/android/soundrecorder/actionmode/ButtonWithPopupMenu.java
new file mode 100644
index 0000000..2f9ce3f
--- /dev/null
+++ b/src/com/android/soundrecorder/actionmode/ButtonWithPopupMenu.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.actionmode;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.PopupMenu;
+
+public class ButtonWithPopupMenu extends Button {
+    private PopupMenu mPopupMenu;
+
+    public ButtonWithPopupMenu(Context context) {
+        this(context, null);
+    }
+
+    public ButtonWithPopupMenu(Context context, AttributeSet attrs) {
+        this(context, attrs, android.R.attr.actionDropDownStyle);
+    }
+
+    public ButtonWithPopupMenu(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void loadPopupMenu(int id, PopupMenu.OnMenuItemClickListener listener) {
+        mPopupMenu = new PopupMenu(getContext(), this);
+        mPopupMenu.getMenuInflater().inflate(id, mPopupMenu.getMenu());
+        mPopupMenu.setOnMenuItemClickListener(listener);
+        setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showMenu();
+            }
+        });
+    }
+
+    private void showMenu() {
+        if (mPopupMenu != null) {
+            mPopupMenu.show();
+        }
+    }
+
+    public MenuItem findPopupMenuItem(int menuItemId) {
+        if (mPopupMenu != null) {
+            return mPopupMenu.getMenu().findItem(menuItemId);
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/FileListActivity.java b/src/com/android/soundrecorder/filelist/FileListActivity.java
new file mode 100644
index 0000000..810da07
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/FileListActivity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.MenuItem;
+import com.android.soundrecorder.R;
+import com.android.soundrecorder.filelist.player.Player;
+import com.android.soundrecorder.filelist.player.PlayerPanel;
+import com.android.soundrecorder.util.PermissionUtils;
+
+public class FileListActivity extends Activity {
+    private Player mPlayer;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.file_list_activity);
+        PlayerPanel playerPanel = (PlayerPanel) findViewById(R.id.player_panel);
+        mPlayer = new Player(getApplicationContext(), playerPanel);
+
+        FileListFragment fragment = new FileListFragment();
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, fragment, FileListFragment.FRAGMENT_TAG)
+                .commit();
+    }
+
+    /** This request result is from FileListFragment. */
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        if (PermissionUtils.checkPermissionResult(permissions, grantResults)) {
+            reloadFragmentAdapter();
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                onBackPressed();
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    public void reloadFragmentAdapter() {
+        Fragment fragment = getFragmentManager().findFragmentByTag(FileListFragment.FRAGMENT_TAG);
+        if (fragment != null && fragment instanceof FileListFragment) {
+            ((FileListFragment) fragment).reloadAdapter();
+        }
+    }
+
+    public Player getPlayer() {
+        return mPlayer;
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (getPlayer() != null) {
+            getPlayer().pausePlayer();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (getPlayer() != null) {
+            getPlayer().stopPlayer();
+        }
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/FileListFragment.java b/src/com/android/soundrecorder/filelist/FileListFragment.java
new file mode 100755
index 0000000..c2be1ed
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/FileListFragment.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist;
+
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import com.android.soundrecorder.RenameDialogBuilder;
+import com.android.soundrecorder.actionmode.ActionModeHandler;
+import com.android.soundrecorder.R;
+import com.android.soundrecorder.filelist.listitem.BaseListItem;
+import com.android.soundrecorder.filelist.listitem.MediaItem;
+import com.android.soundrecorder.filelist.player.Player;
+import com.android.soundrecorder.filelist.player.PlayerPanel;
+import com.android.soundrecorder.util.DatabaseUtils;
+import com.android.soundrecorder.util.FileUtils;
+import com.android.soundrecorder.util.PermissionUtils;
+import com.android.soundrecorder.util.StorageUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import android.util.Log;
+import android.os.Build;
+
+public class FileListFragment extends Fragment {
+    private static final String TAG = "FileListFragment";
+
+    private static final int PERMISSION_REQUEST_CODE = 1;
+    public static final String FRAGMENT_TAG = "FileListFragment";
+    public static final String FOLDER_PATH = "folder_path";
+    public static final String MIME_TYPE_AUDIO = "audio/*";
+    private FileListRecyclerView mRecyclerView;
+    private FileListRecyclerAdapter mAdapter;
+
+    private boolean mIsRootPage = true;
+    private String mArgumentPath;
+
+    //fix the rename failure when reload adapter during renaming.
+    private AlertDialog mRenameDialog;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        initArguments(getArguments());
+        initAdapter();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        // FileListActivity will receive the result.
+        if (PermissionUtils.checkPermissions(getActivity(), PermissionUtils.PermissionType.PLAY,
+                PERMISSION_REQUEST_CODE)) {
+            if (mRenameDialog != null && mRenameDialog.isShowing()) {
+                mRenameDialog.dismiss();
+                mRenameDialog = null;
+            }
+            reloadAdapter();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        resetActionBarTitle();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.file_list_fragment, container, false);
+
+        final View recyclerView = rootView.findViewById(R.id.recycler_view);
+        mRecyclerView = (FileListRecyclerView) recyclerView;
+        mRecyclerView.setEmptyView(rootView.findViewById(R.id.list_empty));
+
+        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
+        mRecyclerView.setLayoutManager(layoutManager);
+
+        mRecyclerView.setAdapter(mAdapter);
+
+        if (getPlayer() != null) {
+            getPlayer().setPlayerPanelLayoutListener(new PlayerPanel.LayoutChangedListener() {
+                @Override
+                public void onLayoutChanged(View view) {
+                    updateRecyclerViewLayout(view);
+                }
+            });
+
+            updateRecyclerViewLayout(getPlayer().getPlayerPanel());
+        }
+
+        return rootView;
+    }
+
+    private void updateRecyclerViewLayout(View anchor) {
+        final RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mRecyclerView
+                .getLayoutParams();
+        boolean isPlayerShown = false;
+
+        if (getPlayer() != null) {
+            isPlayerShown = getPlayer().isPlayShown();
+        }
+
+        if (isPlayerShown && anchor != null) {
+            params.bottomMargin = anchor.getMeasuredHeight();
+        } else {
+            params.bottomMargin = 0;
+        }
+        mRecyclerView.post(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.setLayoutParams(params);
+            }
+        });
+    }
+
+    private Player getPlayer() {
+        return ((FileListActivity) getActivity()).getPlayer();
+    }
+
+    private void initArguments(Bundle arguments) {
+        if (arguments != null) {
+            mArgumentPath = arguments.getString(FOLDER_PATH);
+        }
+        mIsRootPage = (mArgumentPath == null);
+    }
+
+    private void initAdapter() {
+        String[] folderArray = null;
+        String[] rootArray = null;
+        if (mIsRootPage) {
+            folderArray = new String[] {
+                    StorageUtils.getCallRecordingStoragePath(),
+                    StorageUtils.getFmRecordingStoragePath()
+            };
+            rootArray = new String[] {
+                    StorageUtils.getPhoneStoragePath()
+            };
+        } else {
+            rootArray = new String[] {
+                    mArgumentPath
+            };
+        }
+        mAdapter = new FileListRecyclerAdapter(getContext(), folderArray, rootArray);
+        mAdapter.setItemListener(new FileListRecyclerAdapter.ItemListener() {
+            @Override
+            public void openItem(BaseListItem item) {
+                if (item.getItemType() == BaseListItem.TYPE_FOLDER) {
+                    startFragment(item.getPath());
+                } else {
+                    if (getPlayer() != null) {
+                        getPlayer().setItem((MediaItem) item);
+                        getPlayer().startPlayer();
+                    }
+                }
+            }
+
+            @Override
+            public void closeItem() {
+                if (getPlayer() != null) {
+                    getPlayer().destroyPlayer();
+                }
+            }
+
+            @Override
+            public MediaItem getPlayingItem() {
+                if (getPlayer() != null) {
+                    return getPlayer().getMediaItem();
+                }
+                return null;
+            }
+
+            @Override
+            public void updatePlayerItem(MediaItem item) {
+                if (getPlayer() != null) {
+                    getPlayer().onItemChanged(item);
+                }
+            }
+        });
+        mAdapter.setActionModeListener(
+                new ActionModeHandler(getActivity(), R.layout.file_list_action_mode,
+                        mActionModeCallback));
+    }
+
+    public void reloadAdapter() {
+        if (mAdapter != null) {
+            mAdapter.reload();
+        }
+    }
+
+    private void resetActionBarTitle() {
+        if (mIsRootPage) {
+            getActivity().setTitle(R.string.file_list_activity_label);
+        } else {
+            if (mArgumentPath != null) {
+                String title = FileUtils.getLastFileName(mArgumentPath, false);
+                if(title.equals(StorageUtils.FM_RECORDING_FOLDER_NAME)){
+                    getActivity().setTitle(R.string.file_list_FM);
+                }
+                else if(title.equals(StorageUtils.CALL_RECORDING_FOLDER_NAME)){
+                    getActivity().setTitle(R.string.file_list_call);
+                }
+                else{
+                    getActivity().setTitle(title);
+                }
+            }
+        }
+    }
+
+    private void startFragment(String path) {
+        FileListFragment fragment = new FileListFragment();
+        FragmentTransaction transaction = getFragmentManager().beginTransaction();
+        transaction.replace(android.R.id.content, fragment);
+        transaction.addToBackStack(path);
+        Bundle bundle = new Bundle();
+        bundle.putString(FOLDER_PATH, path);
+        fragment.setArguments(bundle);
+        transaction.commit();
+    }
+
+    private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
+        @Override
+        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
+            return false;
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode actionMode) {
+            if (mAdapter != null) {
+                mAdapter.leaveSelectionMode();
+            }
+        }
+
+        @Override
+        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
+            actionMode.getMenuInflater().inflate(R.menu.list_action_mode_menu, menu);
+            return true;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
+            switch (menuItem.getItemId()) {
+                case R.id.action_edit:
+                    if (mAdapter != null) {
+                        List<BaseListItem> items = mAdapter.getSelectedItems();
+                        if (items.size() > 0) {
+                            renameItem(items.get(0));
+                        }
+                    }
+                    return true;
+                case R.id.action_share:
+                    if (mAdapter != null) {
+                        shareItems(mAdapter.getSelectedItems());
+                    }
+                    return true;
+                case R.id.action_delete:
+                    if (mAdapter != null) {
+                        deleteItems(mAdapter.getSelectedItems());
+                    }
+                    return true;
+                case R.id.action_select_all:
+                    if (mAdapter != null) {
+                        mAdapter.toggleSelectAllState();
+                    }
+                    return true;
+                default:
+                    break;
+            }
+            return false;
+        }
+    };
+
+    private void renameItem(final BaseListItem item) {
+        final File file = new File(item.getPath());
+        RenameDialogBuilder builder = new RenameDialogBuilder(getContext(), file);
+        builder.setTitle(R.string.rename_dialog_title);
+        builder.setNegativeButton(R.string.discard, null);
+        builder.setPositiveButton(R.string.rename_dialog_save,
+                new RenameDialogBuilder.OnPositiveListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which, String newName) {
+                        if (!FileUtils.exists(file)) {
+                            Toast.makeText(getContext(), R.string.file_deleted, Toast.LENGTH_SHORT)
+                                    .show();
+                            return;
+                        }
+                        // rename file.
+                        File newFile = FileUtils.renameFile(file, newName);
+                        // update database.
+                        DatabaseUtils.rename(getContext(), file, newFile);
+
+                        item.setPath(newFile.getAbsolutePath());
+                        // update list item.
+                        item.setTitle(newName);
+                        if (mAdapter != null) {
+                            mAdapter.notifyItemChanged(item);
+                        }
+                        if (getPlayer() != null && getPlayer().isItemUsing(item)) {
+                            getPlayer().onItemChanged((MediaItem) item);
+                        }
+                    }
+                });
+        builder.setEditTextContent(FileUtils.getLastFileName(file, false));
+        mRenameDialog = builder.show();
+    }
+
+    private void deleteItems(final List<BaseListItem> items) {
+        String message = getResources().getQuantityString(R.plurals.delete_selection_message,
+                items.size());
+
+        AlertDialog dialog = new AlertDialog.Builder(getContext())
+                .setMessage(message)
+                .setNegativeButton(android.R.string.cancel, null)
+                .setPositiveButton(R.string.delete_dialog_confirm,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                for (BaseListItem item : items) {
+                                    deleteItem(item);
+                                    if (getPlayer() != null && getPlayer().isItemUsing(item)) {
+                                        getPlayer().onItemDeleted();
+                                    }
+                                }
+                            }
+                        })
+                .create();
+        dialog.show();
+        Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
+        if (negativeButton != null) {
+            negativeButton.setTextColor(getContext().getColor(R.color.negative_button_color));
+        }
+    }
+
+    private void deleteItem(final BaseListItem item) {
+        File file = new File(item.getPath());
+        boolean itemRemoved = FileUtils.deleteFile(file, getContext());
+
+        if (itemRemoved && mAdapter != null) {
+            mAdapter.removeItem(item);
+        }
+    }
+
+    private void shareItems(final List<BaseListItem> items) {
+        // generate uris
+        final ArrayList<Uri> uris = new ArrayList<Uri>();
+        for (BaseListItem item : items) {
+            File file = new File(item.getPath());
+            if (file.isDirectory()) {
+                uris.addAll(FileUtils.urisFromFolder(file, getContext()));
+            } else {
+                Uri uri = null;
+                if (Build.VERSION.SDK_INT >= 24) {
+                    uri = FileUtils.uriFromFile(file, getContext());
+                } else {
+                    uri = Uri.fromFile(file);
+                }
+                uris.add(uri);
+            }
+        }
+        // generate intent
+        final Intent shareIntent = new Intent();
+
+        int size = uris.size();
+        if (size == 0) return;
+        if (size > 1) {
+            shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE).setType(MIME_TYPE_AUDIO);
+            shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
+        } else {
+            shareIntent.setAction(Intent.ACTION_SEND).setType(MIME_TYPE_AUDIO);
+            shareIntent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
+        }
+        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+        String shareTitle = getResources().getString(R.string.share_title);
+        getActivity().startActivity(Intent.createChooser(shareIntent, shareTitle));
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/FileListRecyclerAdapter.java b/src/com/android/soundrecorder/filelist/FileListRecyclerAdapter.java
new file mode 100644
index 0000000..1fa98bc
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/FileListRecyclerAdapter.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.util.Log;
+
+import com.android.soundrecorder.R;
+import com.android.soundrecorder.filelist.listitem.BaseListItem;
+import com.android.soundrecorder.filelist.listitem.FolderItem;
+import com.android.soundrecorder.filelist.listitem.MediaItem;
+import com.android.soundrecorder.filelist.viewholder.BaseViewHolder;
+import com.android.soundrecorder.filelist.viewholder.FolderItemViewHolder;
+import com.android.soundrecorder.filelist.viewholder.MediaItemViewHolder;
+import com.android.soundrecorder.util.DatabaseUtils;
+import com.android.soundrecorder.util.FileUtils;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileListRecyclerAdapter extends RecyclerView.Adapter {
+    private String[] mTargetFolderArray;
+    private String[] mTargetSourceArray;
+    private static final String TAG = "FileListRecyclerAdapter";
+
+    private ContentResolver mContentResolver;
+
+    // store all items
+    private List<BaseListItem> mItemsList = new ArrayList<>();
+
+    private boolean mInSelectionMode = false;
+
+    public interface ItemListener {
+        void openItem(BaseListItem item);
+        void closeItem();
+        MediaItem getPlayingItem();
+        void updatePlayerItem(MediaItem item);
+    }
+
+    private ItemListener mItemListener;
+
+    public void setItemListener(ItemListener listener) {
+        mItemListener = listener;
+    }
+
+    public interface ActionModeListener {
+        void showActionMode();
+        void exitActionMode();
+        void setSelectedCount(int selectedCount, int totalCount, List<BaseListItem> items);
+    }
+
+    private ActionModeListener mActionModeListener;
+
+    public void setActionModeListener(ActionModeListener listener) {
+        mActionModeListener = listener;
+    }
+
+    public FileListRecyclerAdapter(Context context, String[] targetFolderArray,
+            String[] targetSourceArray) {
+        mContentResolver = context.getContentResolver();
+        mTargetFolderArray = targetFolderArray;
+        mTargetSourceArray = targetSourceArray;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (mItemsList != null && mItemsList.size() > position) {
+            return mItemsList.get(position).getItemType();
+        }
+        return BaseListItem.TYPE_NONE;
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        if (viewType == BaseListItem.TYPE_FOLDER) {
+            View folderItem = LayoutInflater.from(viewGroup.getContext()).inflate(
+                    R.layout.file_list_item_folder, null);
+            folderItem.setLayoutParams(lp);
+            return new FolderItemViewHolder(folderItem, R.id.file_list_folder_layout);
+        } else {
+            View recordingItem = LayoutInflater.from(viewGroup.getContext()).inflate(
+                    R.layout.file_list_item_recording, null);
+            recordingItem.setLayoutParams(lp);
+            return new MediaItemViewHolder(recordingItem, R.id.file_list_recording_layout);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
+        if (viewHolder instanceof BaseViewHolder) {
+            if (mItemsList != null && mItemsList.size() > position) {
+                ((BaseViewHolder)viewHolder).setSelectionMode(mInSelectionMode);
+                ((BaseViewHolder)viewHolder).setItem(mItemsList.get(position));
+                ((BaseViewHolder)viewHolder)
+                        .setViewHolderItemListener(new BaseViewHolder.ViewHolderItemListener() {
+                            @Override
+                            public void onItemChecked(BaseListItem item, boolean isChecked) {
+                                changeSelectedState(viewHolder.getAdapterPosition(), isChecked);
+                            }
+
+                            @Override
+                            public void onItemClick(BaseListItem item) {
+                                if (mItemListener != null) {
+                                    mItemListener.openItem(item);
+                                }
+                            }
+
+                            @Override
+                            public void onItemLongClick(BaseListItem item) {
+                                enterSelectionMode(viewHolder.getAdapterPosition());
+                            }
+                        });
+
+                setMediaItemPlayStatusListener(position);
+            }
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return mItemsList == null ? 0 : mItemsList.size();
+    }
+
+    public int getSelectableItemCount() {
+        int count = 0;
+        for (BaseListItem item : mItemsList) {
+            count = count + (item.isSelectable() ? 1 : 0);
+        }
+        return count;
+    }
+
+    private int getSelectedItemCount() {
+        return getSelectedItems().size();
+    }
+
+    public List<BaseListItem> getSelectedItems() {
+        List<BaseListItem> items = new ArrayList<>();
+        for (BaseListItem item : mItemsList) {
+            if (item.isChecked()) {
+                items.add(item);
+            }
+        }
+        return items;
+    }
+
+    public void reload() {
+        List<BaseListItem> resultList = new ArrayList<>();
+        // find folder item
+        if (mTargetFolderArray != null) {
+            for (String folder : mTargetFolderArray) {
+                boolean isEmpty = FileUtils.isFolderEmpty(folder);
+                if (!isEmpty) {
+                    long folderId = DatabaseUtils.getFolderId(mContentResolver, folder);
+                    if (folderId != DatabaseUtils.NOT_FOUND) {
+                        FolderItem item = new FolderItem(folderId, folder);
+                        resultList.add(item);
+                    }
+                }
+            }
+        }
+
+        // find recording item
+        List<Long> sourceFolderIds = new ArrayList<>();
+        if (mTargetSourceArray != null) {
+            for (String folder : mTargetSourceArray) {
+                long folderId = DatabaseUtils.getFolderId(mContentResolver, folder);
+                if (folderId != DatabaseUtils.NOT_FOUND) {
+                    sourceFolderIds.add(folderId);
+                }
+            }
+        }
+
+        if (sourceFolderIds.size() > 0) {
+            Long ids[] = new Long[sourceFolderIds.size()];
+            sourceFolderIds.toArray(ids);
+            Cursor cursor = DatabaseUtils.getFolderCursor(mContentResolver, ids);
+            if (cursor != null) {
+                int len = cursor.getCount();
+                for (int i = 0; i < len; i++) {
+                    cursor.moveToNext();
+                    WeakReference<Cursor> cursorWeakReference = new WeakReference<>(cursor);
+                    MediaItem item = new MediaItem(cursorWeakReference.get());
+                    resultList.add(item);
+                }
+                cursor.close();
+            }
+        }
+
+        // update list item to mItemsList.
+        for (BaseListItem item : resultList) {
+            int index = mItemsList.indexOf(item);
+            if (index >= 0) {
+                item.copyFrom(mItemsList.get(index));
+            }
+        }
+        mItemsList.clear();
+        mItemsList.addAll(resultList);
+
+        updatePlayerItem();
+
+        updateSelectedCountInActionMode();
+
+        notifyDataSetChanged();
+    }
+
+    private void updatePlayerItem() {
+        if (mItemListener == null) return;
+        MediaItem playingItem = mItemListener.getPlayingItem();
+        if (playingItem == null || !FileUtils.exists(new File(playingItem.getPath()))) {
+            mItemListener.closeItem();
+        } else {
+            int index = mItemsList.indexOf(playingItem);
+            if (index >= 0) {
+                BaseListItem item = mItemsList.get(index);
+                if (item instanceof MediaItem) {
+                    mItemListener.updatePlayerItem((MediaItem) item);
+                }
+            }
+        }
+    }
+
+    private void enterSelectionMode(int startPosition) {
+        if (mInSelectionMode) return;
+
+        if (mItemListener != null) {
+            mItemListener.closeItem();
+        }
+
+        if (mActionModeListener != null) {
+            mActionModeListener.showActionMode();
+        }
+        mInSelectionMode = true;
+        changeSelectedState(startPosition, true);
+        notifyDataSetChanged();
+    }
+
+    public void leaveSelectionMode() {
+        mInSelectionMode = false;
+        int count = getItemCount();
+        for (int i = 0; i < count; i++) {
+            changeSelectedState(i, false);
+        }
+        notifyDataSetChanged();
+    }
+
+    private void updateSelectedCountInActionMode() {
+        if (mActionModeListener != null) {
+            int selectedCount = getSelectedItemCount();
+            if (selectedCount == 0 || getSelectableItemCount() == 0) {
+                mActionModeListener.exitActionMode();
+            } else {
+                mActionModeListener.setSelectedCount(selectedCount, getSelectableItemCount(),
+                        getSelectedItems());
+            }
+        }
+    }
+
+    public void toggleSelectAllState() {
+        if (!mInSelectionMode) return;
+        boolean isAll = getSelectedItemCount() >= getSelectableItemCount();
+
+        int count = getItemCount();
+        for (int i = 0; i < count; i++) {
+            changeSelectedState(i, !isAll);
+        }
+        notifyDataSetChanged();
+    }
+
+    private void changeSelectedState(int position, boolean checked) {
+        BaseListItem item = mItemsList.get(position);
+        item.setChecked(checked);
+
+        if (mInSelectionMode) {
+            updateSelectedCountInActionMode();
+        }
+    }
+
+    public void notifyItemChanged(BaseListItem item) {
+        if (mItemsList.contains(item)) {
+            int position = mItemsList.indexOf(item);
+            notifyItemChanged(position);
+        }
+    }
+
+    public void removeItem(BaseListItem item) {
+        if (mItemsList.contains(item)) {
+            int position = mItemsList.indexOf(item);
+            mItemsList.remove(item);
+            notifyItemRemoved(position);
+        }
+
+        if (mInSelectionMode) {
+            updateSelectedCountInActionMode();
+        }
+    }
+
+    private void setMediaItemPlayStatusListener(final int position) {
+        if (BaseListItem.TYPE_MEDIA_ITEM != getItemViewType(position)) {
+            return;
+        }
+
+        BaseListItem baseItem = mItemsList.get(position);
+        if ((baseItem == null) || !(baseItem instanceof MediaItem)) {
+            return;
+        }
+
+        ((MediaItem) baseItem).setItemPlayStatusListener(
+                new MediaItem.ItemPlayStatusListener() {
+
+                    @Override
+                    public void onPlayStatusChanged(MediaItem.PlayStatus status) {
+                        FileListRecyclerAdapter.this.notifyItemChanged(position);
+                    }
+                }
+        );
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/FileListRecyclerView.java b/src/com/android/soundrecorder/filelist/FileListRecyclerView.java
new file mode 100644
index 0000000..2d2e82b
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/FileListRecyclerView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class FileListRecyclerView extends RecyclerView {
+    private View mEmptyView;
+
+    private AdapterDataObserver mEmptyDataObserver = new AdapterDataObserver() {
+        @Override
+        public void onChanged() {
+            showEmptyViewIfNeed();
+        }
+    };
+
+    public FileListRecyclerView(Context context) {
+        this(context, null);
+    }
+
+    public FileListRecyclerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FileListRecyclerView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public void setAdapter(Adapter adapter) {
+        super.setAdapter(adapter);
+
+        if (adapter != null) {
+            adapter.registerAdapterDataObserver(mEmptyDataObserver);
+        }
+
+        mEmptyDataObserver.onChanged();
+    }
+
+    public boolean isAdapterEmpty() {
+        Adapter adapter = getAdapter();
+        return (adapter == null || adapter.getItemCount() == 0);
+    }
+
+    private void showEmptyViewIfNeed() {
+        if (mEmptyView == null)
+            return;
+        if (isAdapterEmpty()) {
+            mEmptyView.setVisibility(View.VISIBLE);
+            FileListRecyclerView.this.setVisibility(View.GONE);
+        } else {
+            mEmptyView.setVisibility(View.GONE);
+            FileListRecyclerView.this.setVisibility(View.VISIBLE);
+        }
+    }
+
+    public void setEmptyView(View emptyView) {
+        mEmptyView = emptyView;
+        showEmptyViewIfNeed();
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/listitem/BaseListItem.java b/src/com/android/soundrecorder/filelist/listitem/BaseListItem.java
new file mode 100644
index 0000000..6f91399
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/listitem/BaseListItem.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.listitem;
+
+public abstract class BaseListItem {
+    public static final int SUPPORT_DELETE = 1 << 0;
+    public static final int SUPPORT_SHARE = 1 << 1;
+    public static final int SUPPORT_EDIT = 1 << 2;
+    public static final int SUPPORT_NONE = 0;
+    public static final int SUPPORT_ALL = 0xffffffff;
+
+    protected long mId;
+    protected String mTitle;
+    protected boolean mChecked;
+    protected String mPath;
+    protected boolean mSelectable = true;
+
+    protected int mItemType;
+    public static final int TYPE_NONE = 0;
+    public static final int TYPE_FOLDER = 1;
+    public static final int TYPE_MEDIA_ITEM = 2;
+
+    protected int mSupportedOperation;
+
+    public long getId() {
+        return mId;
+    }
+
+    public void setId(long id) {
+        mId = id;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public void setTitle(String title) {
+        mTitle = title;
+    }
+
+    public String getPath() {
+        return mPath;
+    }
+
+    public void setPath(String path) {
+        mPath = path;
+    }
+
+    public boolean isChecked() {
+        return mSelectable && mChecked;
+    }
+
+    public void setChecked(boolean checked) {
+        mChecked = checked;
+    }
+
+    public int getItemType() {
+        return mItemType;
+    }
+
+    protected void setItemType(int itemType) {
+        mItemType = itemType;
+    }
+
+    public boolean isSelectable() {
+        return mSelectable;
+    }
+
+    protected void setSelectable(boolean selectable) {
+        mSelectable = selectable;
+    }
+
+    public int getSupportedOperation() {
+        return mSupportedOperation;
+    }
+
+    protected void setSupportedOperation(int operation) {
+        mSupportedOperation = operation;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BaseListItem) {
+            return getId() == ((BaseListItem) o).getId();
+        } else {
+            return super.equals(o);
+        }
+    }
+
+    public void copyFrom(BaseListItem item) {
+        setTitle(item.getTitle());
+        setPath(item.getPath());
+        setItemType(item.getItemType());
+        setSelectable(item.isSelectable());
+        setSupportedOperation(item.getSupportedOperation());
+        setChecked(item.isChecked());
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/listitem/FolderItem.java b/src/com/android/soundrecorder/filelist/listitem/FolderItem.java
new file mode 100644
index 0000000..181a701
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/listitem/FolderItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.listitem;
+
+public class FolderItem extends BaseListItem {
+    public FolderItem(long id, String path) {
+        setItemType(BaseListItem.TYPE_FOLDER);
+        setId(id);
+        setPath(path);
+        setSelectable(true);
+        setSupportedOperation(SUPPORT_SHARE | SUPPORT_DELETE);
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/listitem/MediaItem.java b/src/com/android/soundrecorder/filelist/listitem/MediaItem.java
new file mode 100644
index 0000000..0b05ff3
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/listitem/MediaItem.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.listitem;
+
+import android.database.Cursor;
+import android.provider.MediaStore;
+import android.util.Log;
+
+public class MediaItem extends BaseListItem {
+    static final String TAG = "MediaItem";
+    private long mDateModified;
+    private long mDuration;
+    public enum PlayStatus {
+        NONE, PLAYING, PAUSE
+    }
+    private PlayStatus mPlayStatus = PlayStatus.NONE;
+
+    public interface ItemPlayStatusListener {
+        void onPlayStatusChanged(MediaItem.PlayStatus status);
+    }
+
+    protected ItemPlayStatusListener mPlayStatusListener;
+
+    public void setItemPlayStatusListener(ItemPlayStatusListener listener) {
+        mPlayStatusListener = listener;
+    }
+
+    public MediaItem(Cursor cursor) {
+        int idIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
+        int dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
+        int titleIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
+        int durationIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION);
+        int modifiedIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATE_MODIFIED);
+        mId = cursor.getLong(idIndex);
+        mPath = cursor.getString(dataIndex);
+        mTitle = cursor.getString(titleIndex);
+        mDuration = cursor.getLong(durationIndex);
+        mDateModified = cursor.getLong(modifiedIndex) * 1000; // second to millisecond
+        setItemType(BaseListItem.TYPE_MEDIA_ITEM);
+        setSelectable(true);
+        setSupportedOperation(SUPPORT_ALL);
+    }
+
+    public long getDateModified() {
+        return mDateModified;
+    }
+
+    public long getDuration() {
+        return mDuration;
+    }
+
+    public PlayStatus getPlayStatus() {
+        return mPlayStatus;
+    }
+
+    public void setPlayStatus(PlayStatus status) {
+        if (mPlayStatus != status) {
+            mPlayStatus = status;
+            if (mPlayStatusListener != null) {
+                mPlayStatusListener.onPlayStatusChanged(status);
+            }
+        }
+    }
+
+    @Override
+    public void copyFrom(BaseListItem item) {
+        super.copyFrom(item);
+        if (item instanceof MediaItem) {
+            mDateModified = ((MediaItem) item).getDateModified();
+            mDuration = ((MediaItem) item).getDuration();
+            setPlayStatus(((MediaItem) item).getPlayStatus());
+        }
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/player/Player.java b/src/com/android/soundrecorder/filelist/player/Player.java
new file mode 100644
index 0000000..88ce4bb
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/player/Player.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.player;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+
+import com.android.soundrecorder.filelist.listitem.BaseListItem;
+import com.android.soundrecorder.filelist.listitem.MediaItem;
+
+import java.io.IOException;
+
+public class Player implements MediaPlayer.OnCompletionListener,MediaPlayer.OnPreparedListener{
+    private Context mApplicationContext;
+
+    private PlayerPanel mPlayerPanel;
+    private MediaItem mMediaItem;
+    private MediaPlayer mPlayer = null;
+    private final static String TAG = "Player";
+
+    private static final int UPDATE_FREQ = 1000;
+    private boolean isPrepared = false;
+
+    private final Handler mHandler = new Handler();
+    private Runnable mUpdateProgress = new Runnable() {
+        public void run() {
+            updateUi();
+        }
+    };
+
+    private MediaPlayer.OnErrorListener mErrorListener = new MediaPlayer.OnErrorListener() {
+        public boolean onError(MediaPlayer mp, int what, int extra) {
+            stopPlayer();
+            return true;
+        }
+    };
+
+    @Override
+    public void onCompletion(MediaPlayer mp) {
+        stopPlayer();
+    }
+
+    public Player(Context context, PlayerPanel playerPanel) {
+        mApplicationContext = context;
+        mPlayerPanel = playerPanel;
+        mPlayerPanel.setPlayerButtonClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mPlayer == null) {
+                    startPlayer();
+                } else if (mPlayer.isPlaying()) {
+                    pausePlayer();
+                } else {
+                    resumePlayer();
+                }
+            }
+        });
+    }
+
+    public void setPlayerPanelLayoutListener(PlayerPanel.LayoutChangedListener listener) {
+        if (mPlayerPanel != null) {
+            mPlayerPanel.setLayoutChangedListener(listener);
+        }
+    }
+
+    public void setItem(MediaItem item) {
+        if (mMediaItem != null) {
+            mMediaItem.setPlayStatus(MediaItem.PlayStatus.NONE);
+        }
+        mMediaItem = item;
+        resetUi();
+    }
+
+    public MediaItem getMediaItem() {
+        return mMediaItem;
+    }
+
+    public boolean isItemUsing(BaseListItem item) {
+        if (item instanceof MediaItem) {
+            return item.equals(mMediaItem);
+        }
+        return false;
+    }
+
+    private void updateUi() {
+        if (mPlayerPanel != null && mPlayer != null) {
+            mPlayerPanel.updateTitle(mMediaItem.getTitle());
+            mPlayerPanel.updateProgress(mPlayer.getCurrentPosition(), mMediaItem.getDuration());
+            boolean isPlaying = mPlayer.isPlaying();
+            mPlayerPanel.updatePlayerStatus(isPlaying);
+            if (isPlaying) {
+                mHandler.postDelayed(mUpdateProgress, UPDATE_FREQ);
+                mMediaItem.setPlayStatus(MediaItem.PlayStatus.PLAYING);
+            } else {
+                mMediaItem.setPlayStatus(MediaItem.PlayStatus.PAUSE);
+            }
+        }
+    }
+
+    private void resetUi() {
+        if (mMediaItem != null) {
+            mMediaItem.setPlayStatus(MediaItem.PlayStatus.PAUSE);
+        }
+
+        if (mPlayerPanel != null) {
+            mPlayerPanel.updateTitle(mMediaItem.getTitle());
+            mPlayerPanel.updateProgress(0, mMediaItem.getDuration());
+            mPlayerPanel.updatePlayerStatus(false);
+        }
+    }
+
+    public void startPlayer() {
+        stopPlayer();
+
+        if (mPlayerPanel != null) {
+            mPlayerPanel.setVisibility(View.VISIBLE);
+        }
+
+        mPlayer = new MediaPlayer();
+
+        try {
+            mPlayer.setDataSource(mMediaItem.getPath());
+        } catch (IOException | IllegalArgumentException | SecurityException | IllegalStateException
+                e) {
+            releasePlayer();
+            return;
+        }
+
+        mPlayer.setOnCompletionListener(this);
+        mPlayer.setOnErrorListener(mErrorListener);
+        mPlayer.setOnPreparedListener(this);
+
+        isPrepared = false;
+        try {
+            mPlayer.prepareAsync();
+        } catch (IllegalStateException e) {
+            releasePlayer();
+            return;
+        }
+
+        updateUi();
+    }
+
+    public void pausePlayer() {
+        if (mPlayer == null || isPrepared == false){
+            return;
+        }
+
+        pause();
+
+        updateUi();
+    }
+
+    @Override
+    public void onPrepared(MediaPlayer mp) {
+        if (mPlayer == null) return;
+
+        //There may be other music stream are playing in background, in order to stop them,
+        //we should get audio focus before start playing
+        int audioFocusResult = requestAudioFocus();
+        if (AudioManager.AUDIOFOCUS_REQUEST_GRANTED != audioFocusResult) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "requestAudioFocus failed!");
+            }
+
+            stopPlayer();
+            return;
+        }
+
+        isPrepared = true;
+        start();
+        updateUi();
+    }
+
+    private void resumePlayer() {
+        if (mPlayer == null || isPrepared == false){
+            return;
+        }
+
+        start();
+        updateUi();
+    }
+
+    public void stopPlayer() {
+        if (mPlayer == null || isPrepared == false){
+            return;
+        }
+        abandonAudioFocus();
+
+        stop();
+        resetUi();
+    }
+
+    public void releasePlayer() {
+        abandonAudioFocus();
+
+        release();
+        resetUi();
+    }
+
+    public void destroyPlayer() {
+        stopPlayer();
+        hidePlayer();
+    }
+
+    protected void hidePlayer() {
+        if (mMediaItem != null) {
+            mMediaItem.setPlayStatus(MediaItem.PlayStatus.NONE);
+        }
+        if (mPlayerPanel != null) {
+            mPlayerPanel.setVisibility(View.GONE);
+        }
+    }
+
+
+    public void stop() {
+        if (mPlayer == null){
+            return;
+        }
+
+        try {
+            mPlayer.stop();
+        } catch(RuntimeException exception) {
+            Log.e(TAG, "stop failed!");
+        }
+
+        mPlayer.release();
+        mPlayer = null;
+    }
+
+    public void release() {
+        if (mPlayer == null){
+            return;
+        }
+
+        mPlayer.reset();
+        mPlayer.release();
+        mPlayer = null;
+    }
+
+    public void start() {
+        if (mPlayer == null){
+            return;
+        }
+
+        try {
+            mPlayer.start();
+        } catch(RuntimeException exception) {
+            Log.e(TAG, "start failed!");
+        }
+    }
+
+    public void pause() {
+        if (mPlayer == null){
+            return;
+        }
+
+        try {
+            mPlayer.pause();
+        } catch(RuntimeException exception) {
+            Log.e(TAG, "pause failed!");
+        }
+    }
+
+    public boolean isPlayShown() {
+        return (mPlayerPanel != null && mPlayerPanel.getVisibility() == View.VISIBLE);
+    }
+
+    public View getPlayerPanel() {
+        return mPlayerPanel;
+    }
+
+    public void onItemDeleted() {
+        stopPlayer();
+        mMediaItem = null;
+        hidePlayer();
+    }
+
+    public void onItemChanged(MediaItem newItem) {
+        mMediaItem = newItem;
+        updateUi();
+    }
+
+    private int requestAudioFocus() {
+        AudioManager am = (AudioManager) mApplicationContext.getSystemService(Context.AUDIO_SERVICE);
+        return am.requestAudioFocus(mAudioFocusChangeListener,
+                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+    }
+
+    private void abandonAudioFocus() {
+        AudioManager am = (AudioManager) mApplicationContext.getSystemService(Context.AUDIO_SERVICE);
+        am.abandonAudioFocus(mAudioFocusChangeListener);
+    }
+
+    private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener
+            = new AudioManager.OnAudioFocusChangeListener() {
+
+        @Override
+        public void onAudioFocusChange(int focusChange) {
+            if (AudioManager.AUDIOFOCUS_LOSS == focusChange) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "audio focus loss, stop player");
+                }
+                stopPlayer();
+            }
+        }
+    };
+}
diff --git a/src/com/android/soundrecorder/filelist/player/PlayerPanel.java b/src/com/android/soundrecorder/filelist/player/PlayerPanel.java
new file mode 100644
index 0000000..5df11bb
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/player/PlayerPanel.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.player;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.soundrecorder.R;
+import com.android.soundrecorder.util.Utils;
+
+public class PlayerPanel extends LinearLayout {
+    private static final int MAX_PROGRESS = 1000;
+    private ProgressBar mProgressBar;
+    private TextView mProgressTimeView;
+    private TextView mProgressTotalView;
+    private TextView mTitleView;
+    private ImageButton mPlayerButton;
+
+    private LayoutChangedListener mLayoutChangedListener;
+
+    public interface LayoutChangedListener {
+        void onLayoutChanged(View view);
+    }
+
+    public void setLayoutChangedListener(LayoutChangedListener listener) {
+        mLayoutChangedListener = listener;
+    }
+
+    public PlayerPanel(Context context) {
+        this(context, null);
+    }
+
+    public PlayerPanel(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PlayerPanel(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        initResources();
+    }
+
+    private void initResources() {
+        mProgressBar = (ProgressBar) findViewById(R.id.player_progress);
+        mProgressTimeView = (TextView) findViewById(R.id.player_progress_time);
+        mProgressTotalView = (TextView) findViewById(R.id.player_total_time);
+        mTitleView = (TextView) findViewById(R.id.player_title);
+        mPlayerButton = (ImageButton) findViewById(R.id.player_controller);
+
+        mProgressBar.setMax(MAX_PROGRESS);
+        updateProgress(0, 0);
+    }
+
+    public void setPlayerButtonClickListener(OnClickListener listener) {
+        mPlayerButton.setOnClickListener(listener);
+    }
+
+    public void updateTitle(String title) {
+        mTitleView.setText(title);
+    }
+
+    public void updateProgress(long current, long total) {
+        mProgressBar.setProgress(total == 0 ? 0 : (int) (current * MAX_PROGRESS / total));
+        // millisecond to second
+        long cur = Math.round(current * 1.0f / 1000);
+        mProgressTimeView.setText(Utils.timeToString(getContext(), cur));
+        mProgressTotalView.setText(Utils.timeToString(getContext(), total / 1000));
+    }
+
+    public void updatePlayerStatus(boolean playing) {
+        if (playing) {
+            mPlayerButton.setImageResource(R.drawable.player_panel_pause);
+        } else {
+            mPlayerButton.setImageResource(R.drawable.player_panel_play);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        if (changed && mLayoutChangedListener != null) {
+            mLayoutChangedListener.onLayoutChanged(this);
+        }
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        int oldVisibility = getVisibility();
+        super.setVisibility(visibility);
+        if (visibility != oldVisibility && mLayoutChangedListener != null) {
+            mLayoutChangedListener.onLayoutChanged(this);
+        }
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/ui/WaveIndicator.java b/src/com/android/soundrecorder/filelist/ui/WaveIndicator.java
new file mode 100644
index 0000000..551eb19
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/ui/WaveIndicator.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.ui;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class WaveIndicator extends View {
+    private static final float SPACE_WEIGHT = 0.5f;
+    private static final float STOP_HEIGHT = 1f / 12;
+    private static final int FRAME_FREQ = 10;
+    private static final int BARS_COUNT = 4;
+
+    private static final int COLOR_DEFAULT_ACTIVATED = Color.RED;
+    private static final int COLOR_DEFAULT_NORMAL = Color.GRAY;
+
+    private float mBars[] = new float[BARS_COUNT];
+    private float mBarsNext[] = new float[BARS_COUNT];
+
+    private boolean mAnimate;
+    private int mFrameIndex;
+
+    private int mColorActivated = COLOR_DEFAULT_ACTIVATED;
+    private int mColorNormal = COLOR_DEFAULT_NORMAL;
+
+    private Paint mPaint;
+
+    public WaveIndicator(Context context) {
+        this(context, null);
+    }
+
+    public WaveIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WaveIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public WaveIndicator(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        TypedArray array = context.getTheme().obtainStyledAttributes(new int[]{
+                android.R.attr.colorControlActivated,
+                android.R.attr.colorControlNormal,
+        });
+        mColorActivated = array.getColor(array.getIndex(0), COLOR_DEFAULT_ACTIVATED);
+        mColorNormal = array.getColor(array.getIndex(1), COLOR_DEFAULT_NORMAL);
+        array.recycle();
+
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    }
+
+    public void animate(boolean animate) {
+        mAnimate = animate;
+        mFrameIndex = 0;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        float height = getHeight();
+        float width = getWidth();
+        float weight = BARS_COUNT + (BARS_COUNT - 1) * SPACE_WEIGHT;
+        float barWidth = width / weight;
+        float spaceWidth = width / weight * SPACE_WEIGHT;
+
+        if (mFrameIndex % FRAME_FREQ == 0) {
+            for (int i = 0; i < mBarsNext.length; i++) {
+                mBarsNext[i] = (float) Math.random();
+            }
+        }
+        mFrameIndex++;
+
+        for (int i = 0; i < mBars.length; i++) {
+            float left = i * (barWidth + spaceWidth);
+            float top = 0;
+            if (mAnimate) {
+                mPaint.setColor(mColorActivated);
+                mBars[i] = mBars[i] + (mBarsNext[i] - mBars[i]) / FRAME_FREQ;
+                top = mBars[i] * height;
+            } else {
+                mPaint.setColor(mColorNormal);
+                top = height - STOP_HEIGHT * height;
+            }
+            canvas.drawRect(left, top, left + barWidth, height, mPaint);
+        }
+
+        if (mAnimate) invalidate();
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/viewholder/BaseViewHolder.java b/src/com/android/soundrecorder/filelist/viewholder/BaseViewHolder.java
new file mode 100644
index 0000000..17968be
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/viewholder/BaseViewHolder.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.viewholder;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import com.android.soundrecorder.R;
+import com.android.soundrecorder.filelist.listitem.BaseListItem;
+
+public class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,
+        View.OnLongClickListener {
+    protected View mRootView;
+    protected TextView mTitleView;
+    protected CheckBox mCheckBox;
+    protected boolean mInSelectionMode = false;
+    protected BaseListItem mItem;
+
+    public interface ViewHolderItemListener {
+        void onItemChecked(BaseListItem item, boolean isChecked);
+        void onItemClick(BaseListItem item);
+        void onItemLongClick(BaseListItem item);
+    }
+
+    protected ViewHolderItemListener mItemListener;
+
+    public void setViewHolderItemListener(ViewHolderItemListener listener) {
+        mItemListener = listener;
+    }
+
+    public BaseViewHolder(View itemView, int rootLayoutId) {
+        super(itemView);
+        mRootView = itemView.findViewById(rootLayoutId);
+        if (mRootView != null) {
+            mRootView.setOnClickListener(this);
+            mRootView.setOnLongClickListener(this);
+        }
+        mTitleView = (TextView)itemView.findViewById(R.id.list_item_title);
+        mCheckBox = (CheckBox)itemView.findViewById(R.id.list_check_box);
+        if (mCheckBox != null) {
+            mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+                @Override
+                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                    if (mItemListener != null) {
+                        mItemListener.onItemChecked(mItem, isChecked);
+                    }
+                }
+            });
+        }
+    }
+
+    public void setItem(BaseListItem item) {
+        mItem = item;
+        setTitle(item.getTitle());
+        if (mItem.isSelectable()) {
+            setSelected(mInSelectionMode && item.isChecked());
+        } else {
+            if (mCheckBox != null) {
+                mCheckBox.setVisibility(View.INVISIBLE);
+            }
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mInSelectionMode) {
+            setSelected(!isSelected());
+        } else {
+            if (mItemListener != null) {
+                mItemListener.onItemClick(mItem);
+            }
+        }
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (!mItem.isSelectable() || mInSelectionMode) {
+            return false;
+        }
+        if (mItemListener != null) {
+            mItemListener.onItemLongClick(mItem);
+            return true;
+        }
+        return false;
+    }
+
+    public void setTitle(String title) {
+        if (mTitleView != null) {
+            mTitleView.setText(title);
+        }
+    }
+
+    public void setSelectionMode(boolean inSelection) {
+        mInSelectionMode = inSelection;
+        if (mCheckBox == null) return;
+        mCheckBox.setVisibility(mInSelectionMode ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    public void setSelected(boolean selected) {
+        if (mCheckBox == null) return;
+        mCheckBox.setChecked(selected);
+    }
+
+    public boolean isSelected() {
+        return mCheckBox != null && mCheckBox.isChecked();
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/viewholder/FolderItemViewHolder.java b/src/com/android/soundrecorder/filelist/viewholder/FolderItemViewHolder.java
new file mode 100644
index 0000000..18ee363
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/viewholder/FolderItemViewHolder.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.viewholder;
+
+import android.view.View;
+
+import com.android.soundrecorder.filelist.listitem.BaseListItem;
+import com.android.soundrecorder.util.FileUtils;
+import com.android.soundrecorder.util.StorageUtils;
+import com.android.soundrecorder.R;
+
+public class FolderItemViewHolder extends BaseViewHolder {
+    public FolderItemViewHolder(View itemView, int rootLayoutId) {
+        super(itemView, rootLayoutId);
+    }
+
+    @Override
+    public void setItem(BaseListItem item) {
+        super.setItem(item);
+
+        String title = FileUtils.getLastFileName(item.getPath(), false);
+        if(title.equals(StorageUtils.FM_RECORDING_FOLDER_NAME)){
+            setTitle(mRootView.getContext().getApplicationContext().getResources()
+                    .getString(R.string.file_list_FM));
+        }
+        else if(title.equals(StorageUtils.CALL_RECORDING_FOLDER_NAME)){
+            setTitle(mRootView.getContext().getApplicationContext().getResources()
+                    .getString(R.string.file_list_call));
+        }
+        else{
+            setTitle(title);
+        }
+    }
+}
diff --git a/src/com/android/soundrecorder/filelist/viewholder/MediaItemViewHolder.java b/src/com/android/soundrecorder/filelist/viewholder/MediaItemViewHolder.java
new file mode 100644
index 0000000..1828fa3
--- /dev/null
+++ b/src/com/android/soundrecorder/filelist/viewholder/MediaItemViewHolder.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.filelist.viewholder;
+
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.soundrecorder.R;
+import com.android.soundrecorder.filelist.listitem.BaseListItem;
+import com.android.soundrecorder.filelist.listitem.MediaItem;
+import com.android.soundrecorder.filelist.ui.WaveIndicator;
+import com.android.soundrecorder.util.Utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class MediaItemViewHolder extends BaseViewHolder {
+    private TextView mDateModifiedView;
+    private TextView mDurationView;
+    private WaveIndicator mWaveIndicator;
+    private SimpleDateFormat mDateFormatter;
+
+    public MediaItemViewHolder(View itemView, int rootLayoutId) {
+        super(itemView, rootLayoutId);
+        mDateModifiedView = (TextView)itemView.findViewById(R.id.list_item_date_modified);
+        mDurationView = (TextView)itemView.findViewById(R.id.list_item_duration);
+        mWaveIndicator = (WaveIndicator) itemView.findViewById(R.id.list_wave_indicator);
+
+        mDateFormatter = new SimpleDateFormat(itemView.getResources().getString(
+                R.string.list_item_date_modified_format), java.util.Locale.US);
+    }
+
+    @Override
+    public void setItem(BaseListItem item) {
+        super.setItem(item);
+        if (item instanceof MediaItem) {
+            long dateModified = ((MediaItem)item).getDateModified();
+            Date date = new Date(dateModified);
+            mDateModifiedView.setText(mDateFormatter.format(date));
+
+            long duration = ((MediaItem)item).getDuration() / 1000; // millisecond to second
+            mDurationView.setText(Utils.timeToString(mDurationView.getContext(), duration));
+
+            updateWaveIndicator(((MediaItem) item).getPlayStatus());
+        }
+    }
+
+    private void updateWaveIndicator(MediaItem.PlayStatus status) {
+        if (status == MediaItem.PlayStatus.PLAYING) {
+            mWaveIndicator.setVisibility(View.VISIBLE);
+            mWaveIndicator.animate(true);
+        } else if (status == MediaItem.PlayStatus.PAUSE) {
+            mWaveIndicator.setVisibility(View.VISIBLE);
+            mWaveIndicator.animate(false);
+        } else {
+            mWaveIndicator.setVisibility(View.GONE);
+            mWaveIndicator.animate(false);
+        }
+    }
+}
diff --git a/src/com/android/soundrecorder/util/DatabaseUtils.java b/src/com/android/soundrecorder/util/DatabaseUtils.java
new file mode 100644
index 0000000..5d83e96
--- /dev/null
+++ b/src/com/android/soundrecorder/util/DatabaseUtils.java
@@ -0,0 +1,286 @@
+/*
+  Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of The Linux Foundation nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package com.android.soundrecorder.util;
+
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import com.android.soundrecorder.R;
+
+import java.io.File;
+
+public class DatabaseUtils {
+    private static final String TAG = "DatabaseUtils";
+    private static final String PLAY_LIST_NAME = "My recordings";
+    public static final String VOLUME_NAME = "external";
+    public static final int NOT_FOUND = -1;
+    public static final Uri FILE_BASE_URI = Uri.parse("content://media/external/file");
+
+    /*
+     * A simple utility to do a query into the databases.
+     */
+    public static Cursor query(ContentResolver resolver, Uri uri, String[] projection,
+            String selection, String[] selectionArgs, String sortOrder) {
+        try {
+            if (resolver == null) {
+                return null;
+            }
+            return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+        } catch (UnsupportedOperationException ex) {
+            ex.printStackTrace();
+            return null;
+        }
+    }
+
+    /*
+     * Add the audioId into the playlist
+       and  maintain the play order in the playlist.
+     */
+    public static void InsertIntoPlaylist(ContentResolver resolver, int audioId, long playlistId) {
+        String[] columns = new String[] {"count(*)"};
+
+        Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(VOLUME_NAME, playlistId);
+        Cursor cursor = query(resolver, uri, columns, null, null, null);
+        if (cursor != null) {
+            cursor.moveToFirst();
+            final int base = cursor.getInt(0);
+            cursor.close();
+
+            ContentValues value = new ContentValues();
+            value.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
+            value.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, base + audioId);
+            resolver.insert(uri, value);
+        }
+    }
+
+    /*
+     * get the id from the audio_playlists table
+     *  for the default play list
+     */
+    public static int getPlaylistId(ContentResolver resolver) {
+        final String where = MediaStore.Audio.Playlists.NAME + "=?";
+        final String[] args = new String[] {
+            PLAY_LIST_NAME
+        };
+
+        final String[] ids = new String[] {
+            MediaStore.Audio.Playlists._ID
+        };
+        Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
+        Cursor cur = query(resolver, uri, ids, where, args, null);
+        if (cur == null) {
+            Log.e(TAG, " the query is null");
+        }
+        int id = NOT_FOUND;
+        if (cur != null) {
+            cur.moveToFirst();
+            if (!cur.isAfterLast()) {
+                id = cur.getInt(0);
+            }
+            cur.close();
+        }
+        return id;
+    }
+
+    /*
+     * Create a playlist
+     */
+    public static Uri createPlaylist(Context context, ContentResolver resolver) {
+        ContentValues cv = new ContentValues();
+        cv.put(MediaStore.Audio.Playlists.NAME, PLAY_LIST_NAME);
+
+        Uri uri = resolver.insert(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, cv);
+        if (uri == null) {
+            new AlertDialog.Builder(context)
+                    .setTitle(R.string.app_name)
+                    .setMessage(R.string.error_mediadb_new_record)
+                    .setPositiveButton(R.string.button_ok, null)
+                    .setCancelable(false)
+                    .show();
+        }
+        return uri;
+    }
+
+    public static boolean isDataExist(ContentResolver resolver, File file) {
+        Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+        final String[] ids = new String[] {
+                MediaStore.Audio.Playlists._ID
+        };
+        final String where = MediaStore.Audio.Playlists.DATA + "=?";
+        final String[] args = new String[] {
+                file.getAbsolutePath()
+        };
+        Cursor cursor = query(resolver, uri, ids, where, args, null);
+
+        if (cursor != null && cursor.getCount() > 0) {
+            cursor.close();
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * add media file to the database ,and return the content uri.
+     */
+    public static Uri addToMediaDB(Context context, File file, long duration, String mimeType) {
+        ContentValues contentValues = new ContentValues();
+        Resources res = context.getResources();
+
+        String fileName = "";
+        if (!"".equals(res.getString(R.string.def_save_name_prefix))) {
+            fileName = file.getAbsolutePath().substring(
+                    file.getAbsolutePath().lastIndexOf("/") + 1, file.getAbsolutePath().length());
+        } else {
+            fileName = FileUtils.getLastFileName(file, false);
+        }
+
+        long time = System.currentTimeMillis();
+        long date = file.lastModified();
+
+        contentValues.put(MediaStore.Audio.Media.ARTIST,
+                res.getString(R.string.audio_db_artist_name));
+        contentValues.put(MediaStore.Audio.Media.DATE_ADDED, (int) (time / 1000));
+        contentValues.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());
+        contentValues.put(MediaStore.Audio.Media.IS_MUSIC, "1");
+        contentValues.put(MediaStore.Audio.Media.DATE_MODIFIED, (int) (date / 1000));
+        contentValues.put(MediaStore.Audio.Media.DURATION, duration);
+        contentValues.put(MediaStore.Audio.Media.TITLE, fileName);
+        contentValues.put(MediaStore.Audio.Media.ALBUM,
+                res.getString(R.string.audio_db_album_name));
+        contentValues.put(MediaStore.Audio.Media.MIME_TYPE, mimeType);
+
+        Log.d(TAG, "Insert audio record: " + contentValues.toString());
+
+        Uri baseUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+        Uri uri;
+
+        ContentResolver resolver = context.getContentResolver();
+        try {
+            uri = resolver.insert(baseUri, contentValues);
+            Log.d(TAG, "MediaStore.Audio.Media.EXTERNAL_CONTENT_URI: " + baseUri+" uri="+uri);
+        } catch (Exception exception) {
+            uri = null;
+        }
+        if (uri == null) {
+            new AlertDialog.Builder(context).setTitle(R.string.app_name)
+                    .setPositiveButton(R.string.button_ok, null)
+                    .setMessage(R.string.error_mediadb_new_record)
+                    .setCancelable(false).show();
+            return null;
+        }
+        if (getPlaylistId(resolver) == NOT_FOUND) {
+            createPlaylist(context, resolver);
+        }
+        int audioId = Integer.valueOf(uri.getLastPathSegment());
+        InsertIntoPlaylist(resolver, audioId, getPlaylistId(resolver));
+
+        // Notify those applications such as Music listening to the
+        // scanner events that a recorded audio file just created.
+        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
+        return uri;
+    }
+
+    public static void rename(Context context, File file, File newFile) {
+        ContentResolver resolver = context.getContentResolver();
+        String title = FileUtils.getLastFileName(newFile, false);
+
+        ContentValues cv = new ContentValues();
+        cv.put(MediaStore.Audio.Media.TITLE, title);
+        cv.put(MediaStore.Audio.Media.DISPLAY_NAME, title);
+        cv.put(MediaStore.Audio.Media.DATA, newFile.getAbsolutePath());
+
+        Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+        String where = MediaStore.Audio.Media.DATA + " = \'" + file.getAbsolutePath() + "\'";
+        resolver.update(base, cv, where, null);
+    }
+
+    public static void delete(Context context, File file) {
+        ContentResolver resolver = context.getContentResolver();
+        Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+        String where = MediaStore.Audio.Media.DATA + " = \'" + file.getAbsolutePath() + "\'";
+        resolver.delete(base, where, null);
+    }
+
+    public static long getFolderId(ContentResolver resolver, String folderPath) {
+        String[] projection = {
+                MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DATA
+        };
+        String selection = MediaStore.Files.FileColumns.DATA + " = \'" + folderPath + "\'";
+        Cursor cursor = DatabaseUtils.query(resolver, FILE_BASE_URI, projection, selection, null,
+                MediaStore.Files.FileColumns._ID + " ASC LIMIT 1");
+        if (cursor != null) {
+            int idIndex = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);
+            if (cursor.getCount() > 0) {
+                cursor.moveToNext();
+                long id = cursor.getInt(idIndex);
+                cursor.close();
+                return id;
+            }
+        }
+        return NOT_FOUND;
+    }
+
+    public static Cursor getFolderCursor(ContentResolver resolver, long folderId) {
+        return getFolderCursor(resolver, new Long[] {folderId});
+    }
+
+    public static Cursor getFolderCursor(ContentResolver resolver, Long[] folderIds) {
+        if (folderIds == null || folderIds.length == 0) {
+            return null;
+        }
+        String[] projection = {
+                MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DATA,
+                MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DURATION,
+                MediaStore.Audio.Media.DATE_MODIFIED, MediaStore.Files.FileColumns.PARENT
+        };
+
+        String selection = MediaStore.Audio.Media.IS_MUSIC + "=1" + " AND "
+                + MediaStore.Files.FileColumns.PARENT + "=";
+        StringBuilder allSelection = new StringBuilder();
+        for (int i = 0; i < folderIds.length; i++) {
+            if (i != 0) {
+                allSelection.append(" OR ");
+            }
+            allSelection.append("(" + selection + "\'" + folderIds[i] + "\'" + ")");
+        }
+        return DatabaseUtils.query(resolver, FILE_BASE_URI, projection, allSelection.toString(),
+                null, MediaStore.Audio.Media.DATE_MODIFIED + " DESC");
+    }
+
+}
diff --git a/src/com/android/soundrecorder/util/FileUtils.java b/src/com/android/soundrecorder/util/FileUtils.java
new file mode 100755
index 0000000..2d471ef
--- /dev/null
+++ b/src/com/android/soundrecorder/util/FileUtils.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.util;
+
+import org.codeaurora.wrapper.soundrecorder.util.LongArrayWrapper;
+import android.content.Context;
+import android.net.Uri;
+
+import java.io.File;
+import java.util.ArrayList;
+import android.content.ContentValues;
+import android.util.Log;
+import android.provider.MediaStore;
+import android.support.v4.content.FileProvider;
+import android.os.Build;
+import android.content.ContentUris;
+import android.database.Cursor;
+import android.content.ContentResolver;
+
+
+public class FileUtils {
+    public static final int NOT_FOUND = -1;
+    public static final int SAVE_FILE_START_INDEX = 1;
+    public static String getLastFileName(File file, boolean withExtension) {
+        if (file == null) {
+            return null;
+        }
+        return getLastFileName(file.getName(), withExtension);
+    }
+
+    public static String getLastFileName(String fileName, boolean withExtension) {
+        if (fileName == null) {
+            return null;
+        }
+        if (!withExtension) {
+            int dotIndex = fileName.lastIndexOf(".");
+            int pathSegmentIndex = fileName.lastIndexOf(File.separator) + 1;
+            if (dotIndex == NOT_FOUND) {
+                dotIndex = fileName.length();
+            }
+            if (pathSegmentIndex == NOT_FOUND) {
+                pathSegmentIndex = 0;
+            }
+            fileName = fileName.substring(pathSegmentIndex, dotIndex);
+        }
+        return fileName;
+    }
+
+    public static String getFileExtension(File file, boolean withDot) {
+        if (file == null) {
+            return null;
+        }
+        String fileName = file.getName();
+        int dotIndex = fileName.lastIndexOf(".");
+        if (dotIndex == NOT_FOUND) {
+            return null;
+        }
+        if (withDot) {
+            return fileName.substring(dotIndex, fileName.length());
+        }
+        return fileName.substring(dotIndex + 1, fileName.length());
+    }
+
+    public static File renameFile(File file, String newName) {
+        if (file == null) {
+            return null;
+        }
+        String filePath = file.getAbsolutePath();
+        String folderPath = file.getParent();
+        String extension = filePath.substring(filePath.lastIndexOf("."), filePath.length());
+        File newFile = new File(folderPath, newName + extension);
+        if (file.renameTo(newFile)) {
+            return newFile;
+        }
+        return file;
+    }
+
+    public static boolean exists(File file) {
+        return file != null && file.exists();
+    }
+
+    public static long getSuitableIndexOfRecording(String prefix) {
+        long returnIndex = SAVE_FILE_START_INDEX;
+        File file = new File(StorageUtils.getPhoneStoragePath());
+        File list[] = file.listFiles();
+        LongArrayWrapper array = new LongArrayWrapper();
+        if (list != null && list.length != 0) {
+            for (File item : list) {
+                String name = getLastFileName(item, false);
+                if (name.startsWith(prefix)) {
+                    int index = prefix.length();
+                    String numString = name.substring(index, name.length());
+                    try {
+                        array.add(Long.parseLong(numString));
+                    } catch (NumberFormatException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        int size = array.size();
+        for (int i = 0; i < size; i++) {
+            if (array.indexOf(returnIndex) >= 0) {
+                returnIndex++;
+            }
+        }
+
+        return returnIndex;
+    }
+
+    public static boolean isFolderEmpty(String filePath) {
+        File file = new File(filePath);
+        File[] files = file.listFiles();
+        return files == null || files.length == 0;
+    }
+
+    public static boolean deleteFile(File file, Context context) {
+        boolean ret = false;
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            for (File f : files) {
+                // remove all files in folder.
+                deleteFile(f, context);
+            }
+        }
+        if (file.delete()) {
+            // update database.
+            DatabaseUtils.delete(context, file);
+            ret = true;
+        }
+        return ret;
+    }
+
+
+    public static Uri uriFromFile(File file, Context context) {
+        if (file == null || context == null) {
+            return null;
+        }
+
+        ContentResolver cr = context.getContentResolver();
+        Cursor c = null;
+        long id = 0;
+        c = cr.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
+                MediaStore.Audio.Media.DATA + "=?", new String[] { file.getAbsolutePath() }, null);
+
+        if(c != null){
+            c.moveToFirst();
+            id = c.getLong(c.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
+        }
+
+        Uri uri = ContentUris.withAppendedId(
+                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
+
+        Log.d("FileUtils","uriFromFile uri="+uri);
+        return uri;
+    }
+
+    public static ArrayList<Uri> urisFromFolder(File folder, Context context) {
+        if (folder == null || !folder.isDirectory()) {
+            return null;
+        }
+
+        ArrayList<Uri> uris = new ArrayList<Uri>();
+        File[] list = folder.listFiles();
+        for (File file : list) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                uris.add(uriFromFile(file,context));
+            } else {
+                uris.add(Uri.fromFile(file));
+            }
+        }
+        return uris;
+    }
+}
diff --git a/src/com/android/soundrecorder/util/PermissionUtils.java b/src/com/android/soundrecorder/util/PermissionUtils.java
new file mode 100755
index 0000000..e2105e9
--- /dev/null
+++ b/src/com/android/soundrecorder/util/PermissionUtils.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.util;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+
+import java.util.ArrayList;
+
+public class PermissionUtils {
+    public enum PermissionType {
+        RECORD, PLAY
+    }
+
+    private static String[] RECORD_PERMISSIONS = {
+            Manifest.permission.READ_PHONE_STATE, Manifest.permission.RECORD_AUDIO,
+            Manifest.permission.CAPTURE_AUDIO_OUTPUT,
+            Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE
+    };
+
+    private static String[] PLAY_PERMISSIONS = {
+            Manifest.permission.READ_EXTERNAL_STORAGE
+    };
+
+    public static String[] getOperationPermissions(PermissionType type) {
+        switch (type) {
+            case RECORD:
+                return RECORD_PERMISSIONS;
+            case PLAY:
+                return PLAY_PERMISSIONS;
+            default:
+                return null;
+        }
+    }
+
+    public static boolean checkPermissions(final Activity activity, PermissionType type,
+            int operationHandle) {
+        String[] permissions = getOperationPermissions(type);
+        return checkPermissions(activity, permissions, operationHandle);
+    }
+
+    public static boolean checkPermissions(final Activity activity, String[] permissions,
+            int operationHandle) {
+        if (permissions == null)
+            return true;
+        boolean isPermissionGranted = true;
+        ArrayList<String> permissionList = new ArrayList<String>();
+        for (String permission : permissions) {
+            if (PackageManager.PERMISSION_GRANTED != activity.checkSelfPermission(permission)) {
+                permissionList.add(permission);
+                isPermissionGranted = false;
+            }
+        }
+
+        if (!isPermissionGranted) {
+            String[] permissionArray = new String[permissionList.size()];
+            permissionList.toArray(permissionArray);
+            activity.requestPermissions(permissionArray, operationHandle);
+        }
+
+        return isPermissionGranted;
+    }
+
+    public static boolean checkPermissionResult(String[] permissions, int[] grantResults) {
+        if (permissions == null || grantResults == null || permissions.length == 0
+                || grantResults.length == 0) {
+            return false;
+        }
+
+        for (int result : grantResults) {
+            if (result != PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/src/com/android/soundrecorder/util/StorageUtils.java b/src/com/android/soundrecorder/util/StorageUtils.java
new file mode 100644
index 0000000..034d08e
--- /dev/null
+++ b/src/com/android/soundrecorder/util/StorageUtils.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.util;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.StatFs;
+import org.codeaurora.wrapper.soundrecorder.util.StorageManagerWrapper;
+import org.codeaurora.wrapper.soundrecorder.util.StorageVolumeWrapper;
+import android.util.Log;
+import com.android.soundrecorder.R;
+
+import java.io.File;
+
+public class StorageUtils {
+    private static final String TAG = "StorageUtils";
+    private static final int VOLUME_SDCARD_INDEX = 1;
+
+    public static final int STORAGE_PATH_PHONE_INDEX = 0;
+    public static final int STORAGE_PATH_SD_INDEX = 1;
+    private static final String FOLDER_NAME = "SoundRecorder";
+    public static final String FM_RECORDING_FOLDER_NAME = "FMRecording";
+    public static final String CALL_RECORDING_FOLDER_NAME = "CallRecord";
+    private static final String STORAGE_PATH_EXTERNAL_ROOT = Environment
+            .getExternalStorageDirectory().toString();
+    private static final String STORAGE_PATH_LOCAL_PHONE =
+            STORAGE_PATH_EXTERNAL_ROOT + File.separator + FOLDER_NAME;
+    private static final String STORAGE_PATH_FM_RECORDING = STORAGE_PATH_EXTERNAL_ROOT
+            + File.separator + FM_RECORDING_FOLDER_NAME;
+    private static final String STORAGE_PATH_CALL_RECORDING = STORAGE_PATH_EXTERNAL_ROOT
+            + File.separator + CALL_RECORDING_FOLDER_NAME;
+    private static String sSdDirectory;
+
+    private static final int SD_STORAGE_FREE_BLOCK = 1;
+    private static final double PHONE_STORAGE_FREE_BLOCK_PERCENT = 5 / 100.0;
+
+    private static String getSdDirectory(Context context) {
+        if (sSdDirectory == null) {
+           sSdDirectory = StorageManagerWrapper.getSdDirectory(context);
+        }
+        return sSdDirectory;
+    }
+
+    public static String getSdState(Context context) {
+        String sdPath = getSdDirectory(context);
+
+        if (sdPath != null) {
+            StorageVolumeWrapper.setBaseInstance(context,new File(sdPath));
+            String state = StorageVolumeWrapper.getState();
+            if (state != null) {
+                return state;
+            } else {
+                return Environment.MEDIA_UNKNOWN;
+            }
+        }
+        return Environment.MEDIA_UNMOUNTED;
+    }
+
+    public static boolean isPhoneStorageMounted() {
+        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
+    }
+
+    public static boolean isSdMounted(Context context) {
+        return getSdState(context).equals(Environment.MEDIA_MOUNTED);
+    }
+
+    public static String applyCustomStoragePath(Context context) {
+        return Environment.getExternalStorageDirectory().toString() + File.separator
+                + context.getResources().getString(R.string.folder_name);
+    }
+
+    public static String getPhoneStoragePath() {
+        return STORAGE_PATH_LOCAL_PHONE;
+    }
+
+    public static String getSdStoragePath(Context context) {
+        return StorageUtils.getSdDirectory(context) + File.separator + FOLDER_NAME;
+    }
+
+    public static String getFmRecordingStoragePath() {
+        return STORAGE_PATH_FM_RECORDING;
+    }
+
+    public static String getCallRecordingStoragePath() {
+        return STORAGE_PATH_CALL_RECORDING;
+    }
+
+    /**
+     * Is there any point of trying to start recording?
+     */
+    public static boolean diskSpaceAvailable(Context context, int path) {
+        return getAvailableBlocks(context, path) > 0;
+    }
+
+    private static long getAvailableBlocks(Context context, int path) {
+        long blocks;
+        if (path == StorageUtils.STORAGE_PATH_SD_INDEX) {
+            StatFs fs = new StatFs(getSdDirectory(context));
+            // keep one free block
+            blocks = fs.getAvailableBlocksLong() - SD_STORAGE_FREE_BLOCK;
+        } else {
+            StatFs fs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
+            // keep 5% free block
+            blocks = fs.getAvailableBlocksLong()
+                    - (long)(fs.getBlockCountLong() * PHONE_STORAGE_FREE_BLOCK_PERCENT);
+        }
+        return blocks;
+    }
+
+    public static long getAvailableBlockSize(Context context, int path) {
+        StatFs fs;
+        if (path == StorageUtils.STORAGE_PATH_SD_INDEX) {
+            fs = new StatFs(getSdDirectory(context));
+        } else {
+            fs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
+        }
+        return getAvailableBlocks(context, path) * fs.getBlockSizeLong();
+    }
+}
diff --git a/src/com/android/soundrecorder/util/Utils.java b/src/com/android/soundrecorder/util/Utils.java
new file mode 100644
index 0000000..b74b869
--- /dev/null
+++ b/src/com/android/soundrecorder/util/Utils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.soundrecorder.util;
+
+import android.content.Context;
+
+import com.android.soundrecorder.R;
+
+public class Utils {
+    static final int SECOND_PER_MINUTES = 60;
+    static final int SECOND_PER_HOUR = 60 * SECOND_PER_MINUTES;
+
+    public static String timeToString(Context context, long time) {
+        long hour = time / SECOND_PER_HOUR;
+        long minutes = time / SECOND_PER_MINUTES - hour * SECOND_PER_MINUTES;
+        long second = time % SECOND_PER_MINUTES;
+
+        String timerFormat;
+        if (hour > 0) {
+            timerFormat = context.getResources().getString(R.string.timer_format_hour);
+            return String.format(timerFormat, hour, minutes, second);
+        } else {
+            timerFormat = context.getResources().getString(R.string.timer_format);
+            return String.format(timerFormat, minutes, second);
+        }
+    }
+}